
活动链接:腾讯位置服务开发者征文大赛——AI赋能 重塑地图智能新体验

项目名称: AI智能地图助手 开发方向: AI对话式地图(自然语言查地点、问路) 技术栈: 腾讯位置服务JSAPI GL + AI大模型 Demo地址: [在线演示链接]
作为开发者,你是否也遇到过这样的场景:
场景1 - 搜索困惑: 用户想找"天安门附近的咖啡馆",传统地图需要:
整个过程至少5步,对于不熟悉地图操作的老人和孩子来说,门槛很高。
场景2 - 路线规划繁琐: 想问"从北京西站到故宫怎么走最快",需要:
为什么不能直接问一句话就得到答案?
场景3 - 个性化推荐缺失: "帮我找个安静、有插座、人少的咖啡馆学习",传统地图只能搜索关键词,无法理解"安静"、"人少"这种抽象需求。

2024年,大语言模型的爆发让自然语言交互成为可能。用户只需说:
AI自动解析意图,调用地图API,返回精准结果——这就是AI对话式地图的价值。


编辑
本项目旨在打造一个AI智能地图助手,实现:
┌─────────────────────────────────────────────────┐
│ 用户界面层 │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ 对话界面 │ │ 地图展示 │ │
│ │ (Chat UI) │◄────►│ (Map Canvas) │ │
│ └──────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────┘
▲
│
┌─────────────────────────────────────────────────┐
│ 应用逻辑层 │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ AI对话服务 │ │ 地图服务 │ │
│ │ (AI Service) │◄────►│ (Map Service) │ │
│ └──────────────┘ └──────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 意图解析 参数提取 操作执行 │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
▲
│
┌─────────────────────────────────────────────────┐
│ API调用层 │
│ ┌────────────────┐ ┌─────────────────┐ │
│ │ 大模型API │ │ 腾讯位置服务API │ │
│ │ (可选云端) │ │ (JSAPI/WebSvc) │ │
│ └────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────┘职责:
设计亮点:
// 支持多种AI引擎混合架构
class AIService {
processInput(input, context) {
switch (this.provider) {
case 'local': // 本地规则引擎(免费、快速)
return this.processLocal(input, context);
case 'qwen': // 通义千问云端API
return this.processQwen(input, context);
case 'zhipu': // 智谱GLM云端API
return this.processZhipu(input, context);
case 'custom': // 自定义API扩展
return this.processCustom(input, context);
}
}
}
意图识别规则示例:
const intents = {
search_poi: [
/找(.+)附近(.+)/, // "找北京西站附近的咖啡馆"
/搜索(.+)/, // "搜索咖啡"
/附近(.+)/ // "附近美食"
],
route_planning: [
/从(.+)到(.+)/, // "从天安门到故宫"
/(.+)到(.+)怎么走/ // "北京西站到故宫怎么走"
],
recommendation: [
/推荐(.+)/, // "推荐景点"
/人少的(.+)/ // "人少的咖啡馆"
]
};
职责:
核心技术选择: 由于浏览器CORS限制,本项目使用 JSONP方式调用WebService API,而非JSAPI服务模块。这样可:
核心API封装:
class MapService {
constructor() {
this.map = null;
this.apiKey = null; // 存储API密钥
this.markers = [];
}
// JSONP请求封装(规避CORS)
jsonpRequest(url) {
return new Promise((resolve, reject) => {
// 创建唯一callback名
const callbackName = 'jsonp_callback_' + Date.now();
const jsonpUrl = url + '&output=jsonp&callback=' + callbackName;
// 创建script标签(天然可跨域)
const script = document.createElement('script');
script.src = jsonpUrl;
// 定义全局callback函数
window[callbackName] = function(data) {
resolve(data);
delete window[callbackName];
document.body.removeChild(script);
};
// 错误和超时处理
script.onerror = () => reject(new Error('请求失败'));
setTimeout(() => {
if (window[callbackName]) {
reject(new Error('请求超时'));
}
}, 10000);
document.body.appendChild(script);
});
}
// POI搜索 - 使用WebService API
async searchPOI(params) {
const keyword = encodeURIComponent(params.keyword);
const region = encodeURIComponent(params.region || '北京');
const apiUrl = `https://apis.map.qq.com/ws/place/v1/search?keyword=${keyword}&boundary=region(${region},0)&key=${this.apiKey}`;
const data = await this.jsonpRequest(apiUrl);
// 详细数据验证
if (data.status !== 0) {
throw new Error(data.message || '搜索失败');
}
// 格式化返回数据
return data.data.map(poi => ({
title: poi.title,
address: poi.address,
location: { lat: poi.location.lat, lng: poi.location.lng },
distance: poi._distance || null
}));
}
// 路线规划 - 使用WebService API
async planRoute(params) {
const apiUrl = `https://apis.map.qq.com/ws/direction/v1/driving/?from=${params.from.lat},${params.from.lng}&to=${params.to.lat},${params.to.lng}&key=${this.apiKey}`;
const data = await this.jsonpRequest(apiUrl);
if (data.status !== 0) {
throw new Error(data.message || '路线规划失败');
}
// 转换数据格式
return {
result: {
routes: data.result.routes.map(route => ({
distance: route.distance,
duration: route.duration,
polyline: route.polyline,
steps: route.steps
}))
}
};
}
// 地理编码 - 地址转坐标
async geocoder(address) {
const apiUrl = `https://apis.map.qq.com/ws/geocoder/v1/?address=${encodeURIComponent(address)}&key=${this.apiKey}`;
const data = await this.jsonpRequest(apiUrl);
// 详细验证返回数据
if (data.status !== 0 || !data.result || !data.result.location) {
throw new Error('未找到地址对应的坐标');
}
return {
lat: data.result.location.lat,
lng: data.result.location.lng
};
}
// 在地图上显示POI
displayPOIOnMap(poiList) {
poiList.forEach((poi, index) => {
// 创建标记
const marker = new TMap.Marker({
map: this.map,
position: new TMap.LatLng(poi.location.lat, poi.location.lng)
});
// 添加信息窗口
const infoWindow = new TMap.InfoWindow({
map: this.map,
position: new TMap.LatLng(poi.location.lat, poi.location.lng),
content: `<h4>${poi.title}</h4><p>${poi.address}</p>`
});
marker.on('click', () => infoWindow.open());
this.markers.push({ marker, infoWindow });
});
}
}
设计亮点:
职责:
完整交互流程:
async function sendMessage() {
const userMessage = inputField.value.trim();
// 1. AI解析意图
const aiResult = await aiService.processInput(userMessage, {
currentLocation: appState.currentLocation,
region: '北京'
});
// 2. 根据意图执行地图操作
switch (aiResult.intent) {
case 'search_poi':
// 搜索POI → 在地图显示
const poiList = await mapService.searchPOI(aiResult.params);
mapService.displayPOIOnMap(poiList);
break;
case 'route_planning':
// 地理编码起点终点 → 路线规划 → 可视化
const from = await mapService.geocoder(aiResult.params.from);
const to = await mapService.geocoder(aiResult.params.to);
const route = await mapService.planRoute({ from, to });
mapService.displayRouteOnMap(route);
break;
}
// 3. 生成友好响应
addMessage('assistant', generateResponse(aiResult, poiList));
}
挑战:用户输入千变万化,如何准确识别意图?
方案:混合策略 - 规则匹配 + 云端大模型
使用正则表达式匹配常见句式:
// 示例:处理"找XX附近XX"
function processLocal(input) {
const match = input.match(/找(.+)附近(.+)/);
if (match) {
return {
intent: 'search_poi',
params: {
location: match[1], // "北京西站"
keyword: match[2] // "咖啡馆"
}
};
}
}
优点:
缺点:
调用通义千问或智谱GLM,使用提示词工程:
const prompt = `
请分析用户地图查询需求,提取关键信息:
用户输入: "${userInput}"
返回JSON格式:
{
"intent": "意图类型(search_poi/route_planning)",
"params": { "keyword": "...", "location": "..." },
"response": "友好的中文响应"
}
`;
const aiResponse = await callQwenAPI(prompt);
const parsed = JSON.parse(aiResponse);
优点:
缺点:
最佳实践:混合架构
// 简单查询用本地引擎
if (isSimpleQuery(input)) {
return processLocal(input);
}
// 复杂查询用云端AI
if (needDeepUnderstanding(input)) {
return processQwen(input);
}
场景:用户说"找北京西站附近的咖啡馆"
技术流程:
ERROR: [Mermaid] Lexical error on line 3. Unrecognized text. ... B --> C[地理编码: 北京西站 → 坐标(39.89, 116.32)] -----------------------^
关键代码:
async function handlePOISearch(params) {
// Step1: 地理编码"北京西站"
const geocodeResult = await mapService.geocoder(
params.location + ', 北京'
);
// Step2: 以该坐标为中心搜索"咖啡馆"
const poiList = await mapService.searchPOI({
keyword: params.keyword,
location: geocodeResult, // 中心点
pageSize: 10
});
// Step3: 在地图上标记,显示距离
mapService.displayPOIOnMap(poiList, {
showDistance: true // 显示"距离XX米"
});
// Step4: 生成文本响应
let response = `找到${poiList.length}个结果:\n`;
poiList.forEach(poi => {
response += `${poi.title} - ${poi.distance}米\n`;
});
}
效果截图:


编辑
场景:用户问"从天安门到故宫怎么走"
实现步骤:
async function handleRoutePlanning(params) {
// 1. 地理编码起点和终点(JSONP方式)
const fromLocation = await mapService.geocoder('天安门, 北京');
const toLocation = await mapService.geocoder('故宫, 北京');
// 2. 调用腾讯WebService路线规划API
const routeResult = await mapService.planRoute({
from: fromLocation,
to: toLocation
// 不传policy参数,使用API默认最优路线
});
// 3. 提取路线信息
const route = routeResult.result.routes[0];
const distance = route.distance; // 米
const duration = route.duration; // 秒
const steps = route.steps; // 导航步骤
// 4. 在地图上绘制路线(JSAPI可视化)
mapService.displayRouteOnMap(routeResult);
// 5. 显示详细信息面板
showRouteInfo({
distance: `${(distance/1000).toFixed(2)}公里`,
duration: `${Math.floor(duration/60)}分钟`,
steps: steps.map(s => s.instruction)
});
}
关键技术架构:
${(distance/1000).toFixed(2)}公里, duration: ${Math.floor(duration/60)}分钟, steps: steps.map(s => s.instruction) }); }
地图渲染效果:
**地图渲染效果**:

---
### 3.4 前端交互优化
#### 设计原则
1. **响应式布局**:对话面板+地图面板并排显示
2. **实时反馈**:AI思考时显示加载动画
3. **历史记录**:保留聊天记录,方便回顾
4. **快捷操作**:提供常用搜索快捷按钮
#### UI实现细节
```css
/* 左右分栏布局 */
.app-container {
display: flex;
height: 100vh;
gap: 20px;
}
.chat-panel {
flex: 0 0 450px; /* 固定宽度 */
background: white;
border-radius: 16px;
}
.map-panel {
flex: 1; /* 自适应剩余空间 */
position: relative;
}
/* 消息动画 */
.message {
animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}


编辑
async function handleRoutePlanning(params) {
// 1. 地理编码起点和终点
const fromLocation = await mapService.geocoder('天安门, 北京');
const toLocation = await mapService.geocoder('故宫, 北京');
// 2. 调用腾讯路线规划API
const routeResult = await mapService.planRoute({
from: fromLocation,
to: toLocation,
policy: TMap.constants.DRIVING_POLICY.LEAST_TIME // 最短时间
});
// 3. 提取路线信息
const route = routeResult.result.routes[0];
const distance = route.distance; // 米
const duration = route.duration; // 秒
const steps = route.steps; // 导航步骤
// 4. 在地图上绘制��线
mapService.displayRouteOnMap(routeResult);
// 5. 显示详细信息面板
showRouteInfo({
distance: `${(distance/1000).toFixed(2)}公里`,
duration: `${Math.floor(duration/60)}分钟`,
steps: steps.map(s => s.instruction)
});
}
问题发现:
项目初期尝试使用JSAPI服务模块进行搜索和路线规划:
// ❌ 尝试1:使用JSAPI服务模块(失败) const searchService = new TMap.service.Search({ pageSize: 10 }); // 报错:Cannot read properties of undefined (reading 'Search')
原因分析:
尝试方案2:改用fetch调用WebService API
// ❌ 尝试2:使用fetch调用WebService API(被CORS拦截) const apiUrl = 'https://apis.map.qq.com/ws/place/v1/search?keyword=咖啡馆&key=YOUR_KEY'; fetch(apiUrl) .then(response => response.json()) .then(data => console.log(data)); // 报错:CORS policy: No 'Access-Control-Allow-Origin' header
原因分析:
最终方案:使用JSONP规避CORS
// ✅ 最终方案:JSONP(成功!)
function jsonpRequest(url) {
return new Promise((resolve, reject) => {
// 1. 创建唯一callback名
const callbackName = 'jsonp_callback_' + Date.now();
const jsonpUrl = url + '&output=jsonp&callback=' + callbackName;
// 2. 创建script标签(天然可跨域)
const script = document.createElement('script');
script.src = jsonpUrl;
// 3. 定义全局callback函数
window[callbackName] = function(data) {
resolve(data);
delete window[callbackName];
document.body.removeChild(script);
};
// 4. 错误和超时处理
script.onerror = () => reject(new Error('请求失败'));
setTimeout(() => {
if (window[callbackName]) {
reject(new Error('请求超时'));
}
}, 10000);
// 5. 执行
document.body.appendChild(script);
});
}
// 使用示例
const apiUrl = 'https://apis.map.qq.com/ws/place/v1/search?keyword=咖啡馆&key=YOUR_KEY';
jsonpRequest(apiUrl).then(data => {
console.log('搜索成功:', data);
});
JSONP原理:
<script> 标签天然可跨域的特性
技术对比:
方案 | JSAPI模块 | fetch | JSONP |
|---|---|---|---|
CORS限制 | ⚠️ 可能受限 | ❌ 受限 | ✅ 无限制 |
白名单配置 | ✅ 必须 | ✅ 必须 | ❌ 不需要 |
兼容性 | ⚠️ 中 | ⚠️ 现代浏览器 | ✅ 所有浏览器 |
稳定性 | ⚠️ 中 | ⚠️ 中 | ✅ 高 |
开发便利 | ⚠️ 中 | ⚠️ 需配置 | ✅ 直接使用 |
适用场景:
问题场景:
开发过程中,经常遇到数据异常导致的错误:
错误1: Cannot read properties of undefined (reading 'lat') 错误2: Cannot read properties of undefined (reading 'map') 错误3: 搜索失败,但无具体原因
解决方案:多层验证 + 详细日志
// ✅ 加强的地理编码验证
async geocoder(address) {
const apiUrl = `https://apis.map.qq.com/ws/geocoder/v1/?address=${address}&key=${apiKey}`;
const data = await this.jsonpRequest(apiUrl);
console.log('地理编码完整响应:', data);
// 第1层:检查API调用是否成功
if (data.status !== 0) {
console.error('地理编码API错误:', data.message);
throw new Error(data.message || '未找到地址对应的坐标');
}
// 第2层:检查result字段
if (!data.result) {
console.error('返回数据缺少result字段:', data);
throw new Error('返回数据格式错误:缺少result字段');
}
// 第3层:检查location字段
if (!data.result.location) {
console.error('返回数据缺少location字段:', data.result);
throw new Error('返回数据格式错误:缺少location字段');
}
const location = data.result.location;
// 第4层:检查坐标类型
if (typeof location.lat !== 'number' || typeof location.lng !== 'number') {
console.error('返回坐标格式错误:', location);
throw new Error(`返回坐标格式错误:lat=${location.lat}, lng=${location.lng}`);
}
// 成功返回
console.log('✅ 地理编码成功:', location);
return { lat: location.lat, lng: location.lng };
}
验证层次:
效果:
问题发现:
用户输入:"推荐几个人少的公园" 提取���键词:"几个公园" ❌ (包含无意义修饰词)
解决方案:修饰词移除列表
// ✅ 关键词智能提取 let keyword = match[1].trim(); // 修饰词列表(包括数量词、形容词) const modifiers = [ '人少的', '好的', '最好的', '好吃的', '好玩的', '几个', '一些', '很多', '附近的', '周边的', '最近的', '最好的', '推荐的', '有名的' ]; // 移除所有修饰词 for (const modifier of modifiers) { keyword = keyword.replace(modifier, ''); } keyword = keyword.trim(); // 结果:"公园" ✅
效果对比:
用户输入 | 原提取结果 | 优化后提取 |
|---|---|---|
"推荐几个人少的公园" | "几个公园" ❌ | "公园" ✅ |
"推荐一些好吃的餐厅" | "一些好吃的餐厅" ❌ | "餐厅" ✅ |
"推荐最好的咖啡馆" | "最好的咖啡馆" ❌ | "咖啡馆" ✅ |
问题场景:
路线规划API报错:"参数错误,以下参数不合法:policy.0"
原因分析:
解决方案:移除policy参数,使用API默认值
// ❌ 错误:传递policy参数 const apiUrl = `...&policy=0&key=...`; // 导致参数错误 // ✅ 正确:不传policy参数 const apiUrl = `https://apis.map.qq.com/ws/direction/v1/driving/?from=${lat},${lng}&to=${lat},${lng}&key=...`; // API默认使用最优路线策略
技术要点:
Console日志分级:
// 信息日志(白色) console.log('JSONP请求URL:', apiUrl); console.log('地理编码完整响应:', data); // 成功日志(白色带✅) console.log('✅ 地理编码成功:', location); console.log('✅ 路线已成功显示在地图上'); // 错误日志(红色) console.error('❌ 搜索失败:', error); console.error('地理编码API错误:', data.message);
调试流程:
1. 检查JSONP请求URL是否正确 2. 查看JSONP响应的status字段 3. 分析完整响应数据结构 4. 检查数据验证各层日志 5. 确认最终返回的数据格式
Network面板检查:
script
output=jsonp&callback=...
jsonp_callback_xxx({...})
用户输入:
帮我找北京西站附近的咖啡馆
AI响应:
好的,正在为您搜索"北京西站"附近的"咖啡馆"... 找到了 10 个相关地点: 1. 星巴克(北京西站店) 📍 北京市丰台区莲花池东路118号 📏 距离约 150 米 2. Costa Coffee(西站南广场) 📍 北京市丰台区莲花池东路 📏 距离约 230 米 ... (地图上已标记所有结果)
地图效果:
用户输入:
从天安门到故宫怎么走
AI响应:
正在为您规划从"天安门"到"故宫"的最佳路线... ✅ 路线规划成功! 📏 总距离: 0.75 公里 ⏱️ 预计时间: 3 分钟 主要路段: • 从起点向东行驶200米 • 右转进入东长安街 • 直行500米到达故宫南门 路线已在地图上显示。
地图效果:
用户输入:
推荐几个人少的公园
AI响应:
正在为您推荐"公园",我会优先推荐评价好、人气高的地点... 找到了 8 个热门公园: 1. 奥林匹克���林公园 📍 北京市朝阳区科荟路 ⭐ 用户评价: 4.8分 | 人流量: 中等 2. 地坛公园 📍 北京市东城区安定门外大街 ⭐ 用户评价: 4.5分 | 人流量: 较少 💡 点击地图标记查看详细信息。
操作类型 | 响应时间 | API调用次数 |
|---|---|---|
本地规则解析 | <50ms | 0次 |
云端AI解析 | 200-500ms | 1次 |
POI搜索 | 100-300ms | 1次地理编码 + 1次搜索 |
路线规划 | 300-600ms | 2次地理编码 + 1次规划 |
优化建议:
用户类型 | 传统痛点 | AI助手优势 |
|---|---|---|
老年用户 | 不懂专业操作 | 口语表达即可 |
儿童 | 输入困难 | 对话式自然 |
外地游客 | 不熟悉地名 | 描述需求即可 |
视障用户 | 屏幕操作难 | 语音输入+语音播报(可扩展) |
用户: 找北京西站附近的咖啡馆 AI: 找到10个结果... 用户: 要有插座、安静的那种 AI: 筛选出3个符合条件的... 用户: 哪个最近? AI: 星巴克距离150米,已导航
将地图能力封装为MCP工具,供AI Agent调用:
{ "tools": [ { "name": "search_poi", "description": "搜索地点", "parameters": ["keyword", "location"] }, { "name": "plan_route", "description": "规划路线", "parameters": ["from", "to"] } ] }
结合用户位置,AI智能推荐最佳汇合地点: 用户A: 在北京西站 用户B: 在天安门 AI: 推荐汇合点: 西单地铁站(距离A 2km, B 3km)
结合热力图、轨迹数据,商业选址分析: 用户: 分析王府井商圈的咖啡馆分布 AI: 生成热力图、竞争分析、选址建议报告
短期目标(1-2个月):
中期目标(3-6个月):
长期愿景:
ai-chat-map/ ├── index.html # 主页面(对话+地图) ├── css/ │ └── style.css # 响应式样式设计 ├── js/ │ ├── map-service.js # 地图服务封装(POI/路线/定位) │ ├── ai-service.js # AI对话服务(意图识别/参数提取) │ └── app.js # 主应用逻辑(交互流程控制) ├── docs/ │ ├── API申请指南.md # 腾讯地图和AI API申请教程 │ └── 技术文章.md # 本参赛文章 └── assets/ └── demo-screenshots/ # 效果截图
https://map.qq.com/api/gljs?v=1.exp
https://dashscope.aliyuncs.com(可选)
https://open.bigmodel.cn(可选)
# 1. 克隆项目��或下载源码) git clone https://github.com/your-repo/ai-chat-map.git # 2. 申请腾讯地图API Key(见docs/API申请指南.md) # 3. 在index.html中替换YOUR_TENCENT_MAP_KEY <script src="https://map.qq.com/api/gljs?v=1.exp&key=YOUR_KEY"></script> # 4. 用浏览器打开index.html即可运行
感谢腾讯位置服务团队提供完善的API生态和技术支持,感谢CSDN组织本次征文活动,让开发者有机会展示创意、分享经验。
呼吁开发者同行:
AI+地图的融合才刚刚开始,潜力巨大!无论是:
都有发挥空间。期待看到更多创新案例,共同推动地图服务从「工具」进化为「智能大脑」!
技术栈: JavaScript + 腾讯位置服务JSAPI GL + AI大模型
欢迎点赞、评论、转发,一起探讨AI+地图的无限可能! 🚀
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。