作者: 黛琳ghz 发布时间: 已于 2026-05-08 03:53:15 修改
来源: https://blog.csdn.net/weixin_53231455/article/details/160869271
💡核心观点:地图不应该是"你输入坐标、它输出路线"的被动工具,而应该是听得懂自然语言、看得见真实道路、会主动规划行程的出行大脑。本文完整呈现从原型到进阶的升级全过程。

腾讯位置服务官网:https://lbs.qq.com
你有没有过这种经历:
问题出在哪?
传统地图的交互逻辑是**“坐标驱动”——你给坐标,它画线。但用户真正的需求是“意图驱动”**的自然语言:「帮我规划一个适合周末亲子的路线,不要太累,有室内外结合,午餐人均 80 以内。」
这就是AI + 地图要解决的核心问题:让地图从"工具"进化为"大脑"。
初版 Demo 存在几个明显短板(自我剖析):


核心升级:AI 决策层现在支持"双引擎"——有 LLM API 走云端,没有则自动降级到本地智能规则引擎,保证 Demo 在任何环境下都可运行。
与 workbuddy 对话过程如下图。

与 workbuddy 对话过程如下图。

升级前:简单字符串匹配,query.includes('亲子')这种方式,覆盖面窄,无法处理语义变体。
升级后:支持 LLM + 本地双引擎,融合度从 40% 提升到 90%+。
// AI_ENGINE.config - 支持任何 OpenAI 兼容接口
config: {
mode: 'llm', // 切换为 llm 模式
apiUrl: 'https://api.deepseek.com/v1/chat/completions',
apiKey: 'YOUR_KEY',
model: 'deepseek-chat'
// 也支持腾讯混元、OpenAI、Qwen 等所有兼容接口
}
Prompt 设计要点(保证结构化输出):
你是一个出行意图解析器。根据用户输入,输出结构化 JSON。
可用城市:北京、上海、广州、杭州、深圳
出行类型:general(通用)、family(亲子)、food(美食)、culture(文化)、tour(旅游)、leisure(休闲)
输出格式:
{
"city": "城市名",
"type": "出行类型",
"preferences": ["偏好1", "偏好2"],
"keywords": ["搜索关键词1", "关键词2"],
"from": "起点名(如无则为null)",
"to": "终点名(如无则为null)",
"transport": "driving/walking/bicycling/transit",
"confidence": 0.95
}
当用户没有配置 LLM API 时,自动降级到扩展后的本地语义映射表:
// 扩展后的语义映射(覆盖 7 种类型 × 3-6 个同义词)
const typeMap = [
{ patterns: ['亲子', '孩子', '小孩', '宝宝', '儿童', '带娃'], type: 'family', prefs: ['亲子友好', '步行距离短', '有室内备选'] },
{ patterns: ['美食', '吃', '餐厅', '小吃', '好吃', '餐饮', '馆子'], type: 'food', prefs: ['特色餐饮', '口碑好'] },
{ patterns: ['一日游', '游玩', '旅游', '旅行', '行程', '打卡'], type: 'tour', prefs: ['景点丰富', '路线合理'] },
{ patterns: ['轻松', '不太累', '休闲', '休闲游', '放松', '佛系', '不赶'], type: 'leisure', prefs: ['轻松休闲', '避免爬山'] },
{ patterns: ['文化', '博物馆', '艺术', '历史', '古迹', '展览'], type: 'culture', prefs: ['文化体验'] },
{ patterns: ['购物', '逛街', '商场', '买东西'], type: 'shopping', prefs: ['购物便利'] },
{ patterns: ['老人', '爸妈', '长辈', '老年', '父母'], type: 'general', prefs: ['轻松休闲', '步行距离短', '避免爬山'] },
];
关键升级:还支持「从 X 到 Y」的起点终点识别正则,以及城市模糊匹配(「魔都」→ 上海,「帝都」→ 北京)。
与 workbuddy 对话过程如下图。

升级前:用直线连接各景点,视觉效果差,用户不信任。
升级后:调用 direction API 获取每段路线的真实道路 polyline,解码后渲染。
WebService API 返回的polyline是前向差分压缩数组:
// Polyline 前向差分解码(腾讯地图 WebService API 特有格式)
function decodePolyline(encoded) {
if (!encoded || encoded.length < 2) return [];
const points = [];
let lat = encoded[0] / 1e6; // 第一个点:绝对纬度
let lng = encoded[1] / 1e6; // 第一个点:绝对经度
points.push({ lat, lng });
// 后续点:整数差值累加
for (let i = 2; i < encoded.length; i += 2) {
lat += encoded[i] / 1e6;
lng += encoded[i + 1] / 1e6;
points.push({ lat, lng });
}
return points;
}
// 逐段规划,获取每段真实道路
async function fetchAndRenderRoute(stops) {
const allPaths = [];
for (let i = 0; i < stops.length - 1; i++) {
const from = stops[i];
const to = stops[i + 1];
const route = await TMAP_WS.direction(from, to, currentMode);
if (route && route.decodedPath && route.decodedPath.length > 0) {
allPaths.push(...route.decodedPath); // 真实道路坐标
} else {
// 降级:直线连接
allPaths.push({ lat: from.lat, lng: from.lng });
allPaths.push({ lat: to.lat, lng: to.lng });
}
}
// 用 MultiPolyline 渲染真实道路
polylineLayer = new TMap.MultiPolyline({
map,
styles: {
route: new TMap.PolylineStyle({
color: '#3777FF', width: 6,
borderWidth: 2, borderColor: '#1A3A80',
lineCap: 'round'
})
},
geometries: [{
id: 'route_real',
styleId: 'route',
paths: allPaths.map(p => new TMap.LatLng(p.lat, p.lng))
}]
});
}
升级前:按popularity字段排序,忽略景点间实际距离,导致路线绕远。
升级后:先调用距离矩阵 API批量计算所有景点间的距离/时间,再用贪心最近邻算法排序。

// 调用距离矩阵 API(一次请求,N×N 矩阵)
const distResult = await TMAP_WS.distanceMatrix(selected, selected, currentMode);
if (distResult) {
distances = distResult.map(row =>
row.elements.map(el => ({
distance: el.distance, // 米
duration: el.duration // 秒
}))
);
}
// 贪心最近邻排序(AI 决策层)
const visited = [0];
const unvisited = new Set(Array.from({ length: n }, (_, i) => i).slice(1));
while (unvisited.size > 0) {
const last = visited[visited.length - 1];
let nearest = -1, nearestDur = Infinity;
for (const idx of unvisited) {
const row = distances[last]?.[idx];
if (row && row.duration < nearestDur) {
nearestDur = row.duration;
nearest = idx;
}
}
if (nearest >= 0) {
visited.push(nearest);
unvisited.delete(nearest);
} else break;
}
itinerary.stops = visited.map(i => itinerary.stops[i]);

效果:用户说「帮我规划北京亲子游」,系统会自动把距离近的景点排在相邻位置,减少绕路。
与 workbuddy 对话过程如下图。

升级前:TMAP_WS.locateByIP()函数已定义但未调用,每次都要手动选城市。
升级后:地图初始化时自动调用 IP 定位,识别用户所在城市并居中显示。
// initMap() 中新增:IP 定位自动获取用户城市
async function initMap() {
// Step1: 获取体验 Key(动态从腾讯地图官网抓取)
const resp = await fetch('https://lbs.qq.com/webApi/uriV1/uriGuide/uriMobileMarker');
const html = await resp.text();
const match = html.match(/referer=([a-zA-Z0-9_-]+)/);
mapKey = match ? match[1] : 'OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77';
// Step2: 加载 JSAPI GL SDK(+ visualization 库)
await loadJSAPI(mapKey);
// Step3: 创建地图实例
map = new TMap.Map('mapContainer', {
center: new TMap.LatLng(39.9042, 116.4074),
zoom: 11
});
// Step4: ⭐ IP 定位(升级新增)
try {
const loc = await TMAP_WS.locateByIP();
if (loc && loc.ad_info && CITY_DATA[loc.ad_info.city]) {
map.setCenter(new TMap.LatLng(
CITY_DATA[loc.ad_info.city].center.lat,
CITY_DATA[loc.ad_info.city].center.lng
));
showToast(`已定位到:${loc.ad_info.city}`, 'success');
}
} catch { /* IP 定位失败不影响使用 */ }
}

升级前:搜索框只有纯文本输入,无提示,用户体验差。
升级后:接入腾讯地图suggestionAPI,输入 2 个字符即触发自动补全,300ms 防抖,点击即填入。
// 搜索框自动补全(对接 suggestion API)
async function handleSuggestion(keyword) {
if (!keyword || keyword.length < 2) {
document.getElementById('suggestionList').classList.remove('show');
return;
}
try {
const results = await TMAP_WS.suggestion(keyword);
const list = document.getElementById('suggestionList');
if (results.length === 0) { list.classList.remove('show'); return; }
// 渲染下拉列表(最多 6 条)
list.innerHTML = results.slice(0, 6).map(r => `
<div class="suggestion-item" data-title="${r.title}">
<div class="sug-title">${r.title}</div>
<div class="sug-addr">${r.address || ''}</div>
</div>
`).join('');
list.classList.add('show');
// 点击补全项,自动填入输入框
list.querySelectorAll('.suggestion-item').forEach(item => {
item.addEventListener('click', () => {
document.getElementById('userInput').value = item.dataset.title;
list.classList.remove('show');
});
});
} catch { /* 自动补全失败不影响体验 */ }
}
// 输入框绑定(300ms 防抖)
input.addEventListener('input', () => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => handleSuggestion(input.value.trim()), 300);
});


升级后的 Demo 实现了以下功能:

基于tencentmap-miniprogram-skill,同样的能力可以快速迁移到微信小程序:
<!-- 小程序地图组件 -->
<map
id="travelMap"
longitude="{{center.lng}}"
latitude="{{center.lat}}"
scale="12"
markers="{{markers}}"
polyline="{{polylines}}"
show-location
bindmarkertap="onMarkerTap"
/>
// 小程序端路线规划(使用 QQMapWX SDK)
const qqmapsdk = new QQMapWX({ key: 'YOUR_KEY' });
qqmapsdk.direction({
mode: 'driving',
from: { latitude: 39.98, longitude: 116.30 },
to: { latitude: 40.01, longitude: 116.40 },
success: (res) => {
const coords = res.result.routes[0].polyline.map(p => ({
latitude: p.lat, longitude: p.lng
}));
this.setData({
polylines: [{ points: coords, color: '#3777FF', width: 6 }]
});
}
});
腾讯地图使用GCJ-02(国测局坐标系),GPS 原始坐标需要通过坐标转换接口处理:
// ⚠️ GPS坐标必须先转换
async function convertGPS(lat, lng) {
const res = await jsonpRequest(
'https://h5gw.map.qq.com/ws/coord/v1/translate',
{
locations: `${lat},${lng}`,
type: 1, // GPS坐标
key: 'none',
apptag: 'lbscoord_translate'
}
);
return res.result.locations[0]; // 转换后的GCJ-02坐标
}
这是腾讯地图 WebService API 的一个特有格式,官方文档描述不够清晰,这里详细说明:
编码规则(前向差分):
polyline[0] = 纬度绝对值 × 10⁶(浮点数)
polyline[1] = 经度绝对值 × 10⁶(浮点数)
polyline[2] = 纬度差值₁ × 10⁶(整数)
polyline[3] = 经度差值₁ × 10⁶(整数)
...
解码步骤:
1. 第一个点:lat = polyline[0] / 1e6, lng = polyline[1] / 1e6
2. 后续每个点:lat += polyline[i] / 1e6, lng += polyline[i+1] / 1e6
常见错误:把 polyline 当成[lat, lng, lat, lng, ...]的简单交替数组,导致坐标完全错误。
使用腾讯位置服务体验 Key 开发时,需注意:
腾讯混元、DeepSeek、OpenAI 的 API 格式略有差异,统一用 OpenAI 兼容格式封装:
// 统一调用入口(支持所有 OpenAI 兼容接口)
async callLLM(messages) {
const res = await fetch(this.config.apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.config.apiKey}`
},
body: JSON.stringify({
model: this.config.model,
messages,
temperature: 0.3,
response_format: { type: 'json_object' } // 强制 JSON 输出
})
});
const data = await res.json();
return JSON.parse(data.choices?.[0]?.message?.content);
}
关键:response_format: { type: 'json_object' }是 OpenAI/DeriveSeek 都支持的参数,可以保证 LLM 输出合法 JSON,避免解析失败。


与 workbuddy 对话过程如下图。

当 AI 和地图深度融合,地图就不再是一个「你查我画」的工具,而是一个能理解你需求、主动给出建议的出行伙伴。这只是开始,未来还可以:
地图的未来,是 AI 的大脑 + 地图的眼睛。
升级后的 Demo 是单文件 HTML,直接双击即可在浏览器中打开运行(使用体验 Key)。
取消AI_ENGINE.config中的注释,填入你的 API Key:
AI_ENGINE.config = {
mode: 'llm',
apiUrl: 'https://api.deepseek.com/v1/chat/completions', // 或混元接口
apiKey: 'YOUR_API_KEY',
model: 'deepseek-chat'
};
将代码中的key: 'none'(体验模式)替换为你的正式 Key,并去掉apptag参数。
📌Demo 在线体验:智能出行规划助手(仅限本地、局域网预览,未部署)
🎯代码资源已上传置文章顶部,可以直接下载运行

📌腾讯位置服务官网:https://lbs.qq.com
📌涉及的 Skill:
与 workbuddy 对话过程如下图。

💡一句话总结:让地图「听懂」你说话,不是魔法,是AI 意图解析 + 腾讯地图 WebService 全链路 API + JSAPI GL 可视化的组合拳。技术不复杂,效果很惊艳——这就是 AI + 地图该有的样子。升级的核心在于:不再模拟,直接调用;不再直线,贴真实道路;不再写死,让 AI 决策。# 【腾讯位置服务开发者征文大赛】让地图听懂人话:AI + 腾讯地图 API 打造智能出行规划助手(升级版)

在智慧出行零售选址物流调度等场景中,精准的地图数据与灵活的接口调用能力是业务落地的关键。腾讯位置服务
在智慧出行本地生活零售选址等场景中,精准的地图能力如定位POI搜索路径规划已成为企业数字化的刚需。腾
作者: 小吉择 发布时间: 已于20260502 17:40:36修改 来源: https:bl