技术栈:腾讯地图 GLJS SDK + SiliconFlow AI API + 原生 JavaScript  

项目类型:单文件Web应用(index.html,约3900行)

目录

  1. [项目背景与痛点分析](#1-项目背景与痛点分析)
  2. [产品核心理念](#2-产品核心理念)
  3. [技术架构与腾讯位置服务集成](#3-技术架构与腾讯位置服务集成)
  4. [创新功能详解](#4-创新功能详解)
  5. [AI能力集成与工程实践](#5-ai能力集成与工程实践)
  6. [关键技术难点与解决方案](#6-关键技术难点与解决方案)
  7. [项目展示与Demo](#7-项目展示与demo)
  8. [总结与展望](#8-总结与展望)

1. 项目背景与痛点分析

1.1 旅行规划的现状痛点

在现代快节奏生活中,规划一次出行往往需要耗费大量时间:打开地图查路线、打开点评找景点、打开备忘录记行程……信息分散在多个App之间,最终得到的往往是一张截图或一条微信消息,到了现场依然一片混乱。

核心痛点有三

  • 信息割裂:路线、景点、时间、天气各自为政,没有一个工具能把它们串起来。
  • 静态规划:传统规划是"死"的——临时改路线?重新查地图;时间不够?手动删节点;错过了景点?行程全乱。
  • 缺乏智能:大多数工具只会"导航",不会"规划"——它们告诉你怎么走,但不会告诉你"应该去哪、待多久、怎么安排最合理"。

1.2 为什么选择腾讯位置服务

腾讯位置服务(Tencent Location Services)提供了完整的地图能力栈:

能力

腾讯位置服务产品

本项目用途

地图渲染

GLJS SDK

高性能WebGL地图展示

路线规划

驾车/公交/步行/骑行路线规划API

多模式路线计算

POI搜索

地点搜索API

沿途推荐地点候选池

坐标解析

地点搜索(Suggestion)

地名→坐标解析

地铁数据

地图底图+线路数据

地铁模式紫色路线展示

2. 产品核心理念

"策略必须是可执行的,而不仅仅是可读的。"

这是本项目设计的出发点。传统旅行攻略是"文章"——你读完了,然后呢?还是要自己打开地图、逐个搜索、手动规划。

本项目的核心理念是:让AI生成的行程,直接变成可交互的地图操作

2.1 三大核心原则

① 结构化,而非文本化

AI输出不再是"建议早上8点从天安门出发,先去故宫……"这样的大段文字,而是严格的结构化JSON:

{

  "summary": "经典皇城一日游,兼顾历史与自然风光",

  "tips": "今日紫外线较强,建议携带防晒用品",

  "nodes": [

    {"type": "start", "place": "天安门广场", "stayMinutes": 0, "tips": "", "transport": "地铁1号线约15分钟"},

    {"type": "waypoint", "place": "故宫博物院", "stayMinutes": 120, "tips": "建议提前网上预约门票", "transport": "步行约10分钟"},

    {"type": "waypoint", "place": "景山公园", "stayMinutes": 60, "tips": "登顶可俯瞰故宫全景", "transport": "驾车约25分钟"},

    {"type": "end", "place": "颐和园", "stayMinutes": 0, "tips": ""}

  ]

}

② 可交互,而非静态展示

每个行程节点都是可操作的卡片:

  • 点击 → 地图飞到对应位置,弹出InfoWindow
  • 修改停留时长 → 实时重算后续时间轴
  • 删除节点 → 路线自动重规划
  • 上下移动 → 调整游玩顺序

③ 双向联动,而非单向展示

地图上点击POI标记,可以直接"插入行程";行程面板里的节点,点击后地图同步定位。地图和行程是双向联动的,而不是两个互不相干的功能。

3. 技术架构与腾讯位置服务集成

3.1 整体架构

┌─────────────────────────────────────────────┐

│                 浏览器前端(单文件应用)          │

│  index.html(~3900行,无构建工具依赖)        │

│                                             │

│  ┌──────────┐  ┌──────────┐  ┌──────────┐ │

│  │ 地图视图  │  │ 行程面板  │  │ AI聊天   │ │

│  │(GLJS SDK)│  │(时间轴) │  │(侧边栏)│ │

│  └────┬─────┘  └────┬─────┘  └────┬─────┘ │

│       │               │               │       │

│  ┌────┴─────────────┴─────────────┴────┐  │

│  │          核心状态管理(appState)         │  │

│  │  waypoints[] / itineraryData / map     │  │

│  └──────────────────┬──────────────────────┘  │

└─────────────────────┼──────────────────────────┘

                      │

         ┌────────────┴────────────┐

         │                         │

  ┌──────┴──────┐         ┌─────┴──────┐

  │ 腾讯位置服务  │         │ SiliconFlow  │

  │ GLJS SDK     │         │ AI API       │

  │ 路线规划API  │         │ (Qwen2.5-32B)│

  │ POI搜索API  │         └────────────┘

  └─────────────┘

3.2 腾讯地图GLJS SDK集成

项目使用腾讯地图GLJS SDK(WebGL渲染),核心集成代码如下:

// 初始化地图

const map = new TMap.Map('map-container', {

  center: new TMap.LatLng(39.9087, 116.3975), // 北京天安门

  zoom: 12,

  viewMode: '2D',

});

// 驾车路线规划(核心功能)

function planRoute() {

  const driving = new TMap.service.DrivingService({

    map: map,

  });

  driving.search({

    from: startLocation,

    to: endLocation,

    waypoints: waypoints, // 支持途经点

    policy: 'REAL_TRAFFIC', // 实时路况

  }).then(response => {

    // 渲染路线 + 沿路线搜索POI

    renderRoute(response.result.routes[0]);

    searchPOIAlongRoute(start, end, keyword, routePath);

  });

}

3.3 地铁模式的特殊处理

腾讯位置服务的Transit API(公交路线规划)对地铁的展示不够直观,因此本项目不用Transit SDK,而是:

  1. 驾车路线规划模拟地铁线路(起点→终点,途经地铁站)
  2. 紫色加粗Polyline渲染,与普通驾车路线区分
  3. 在地图上标注地铁站出入口POI

4. 创新功能详解

4.1 功能一:AI推荐途径点(先推荐,后规划)

这是本项目最具创新性的功能。传统路线规划是"填起终点→直接出路线",但用户往往不知道沿途有什么值得停的地方

新流程(方案C)

用户输入起终点

       ↓

系统规划基础路线

       ↓

沿路线搜索POI(候选池)

       ↓

AI筛选推荐(5~8个精选地点)

       ↓

用户勾选想要去的途径点

       ↓

AI生成完整行程(含时间推算)

核心代码——AI推荐函数

async function aiRecommendPOIs(pois, start, end, durationSec, distanceM) {

  // 构建候选列表(最多20个,避免token超限)

  const candidates = pois.slice(0, 20).map((p, i) =>

    `${i+1}. ${p.title}(类别:${p.category},距路线约${p._routeDist}m)`

  ).join('\n');


  const prompt = `用户从"${start.name}"到"${end.name}",${durationMin}分钟${distKm}公里。

从候选中挑5个最值得停留的:

${candidates}

只返回JSON数组:[{"index":1,"reason":"理由","stayMinutes":60}]`;


  const response = await fetch(SILICONFLOW_API_URL, {

    method: 'POST',

    headers: {

      'Content-Type': 'application/json',

      'Authorization': `Bearer ${SILICONFLOW_API_KEY}`,

    },

    body: JSON.stringify({

      model: 'Qwen/Qwen2.5-32B-Instruct',

      messages: [

        { role: 'system', content: '只返回纯JSON数组,不要包含markdown标记或任何其他文字。' },

        { role: 'user', content: prompt }

      ],

      stream: false,

      temperature: 0.2,

      max_tokens: 500,

    }),

  });

  // ...解析推荐结果,渲染可勾选面板

}

用户侧体验

推荐面板展示每个推荐地点的:

  • 地点名称 + 类别标签
  • AI生成的推荐理由(15字以内)
  • 建议停留时间
  • 勾选框——用户按需选择,不强制全选

4.2 功能二:可设定出发时间的动态时间轴

行程面板顶部有出发时间输入框,用户可以:

  • 使用当前时间(默认)
  • 设置为明天早上8点(规划未来行程)
  • 时间轴根据出发时间动态重算

时间轴效果

08:00  天安门广场(起点)

        ↓ 地铁1号线 约15分钟

08:15  故宫博物院

        停留 120 分钟

        ↓ 步行 约10分钟

10:15  景山公园

        停留 60 分钟

        ↓ 驾车 约25分钟

11:40  颐和园(终点)

4.3 功能三:AI推荐玩法(生成式行程建议)

除了结构化行程,AI还会生成文字版的游玩建议,融入天气、时间、景点特色:

// generateTravelStrategy 函数中的prompt核心部分

const prompt = `请为以下行程生成JSON行程规划。


今天:${todayStr} ${timeStr},天气:${weatherText}

起点:${start.name}

终点:${end.name}

交通:${modeLabel},约${durationMin}分钟


严格按此JSON格式返回:

{"summary":"一句话建议","tips":"天气提示","nodes":[...]}

规则:

1. 必须以{开头}结尾,纯JSON,无markdown

2. nodes首尾必须是start和end

3. 最多5个waypoint节点

4. stayMinutes:景区60-120,餐饮45,普通30

5. 最后一节点无transport

6. 所有字符串值必须用双引号`;

5. AI能力集成与工程实践

5.1 为什么选择SiliconFlow + Qwen2.5-32B

方案

优点

缺点

最终选择

直调腾讯位置服务API

无额外成本

无AI能力

调用OpenAI GPT-4o

能力强

需科学上网、收费高

调用文心一言

国内访问

需百度账号、配额有限

SiliconFlow Qwen2.5-32B

国内直连、JSON输出稳定、性价比高

需API Key

5.2 JSON输出稳定性保障(工程重点)

AI生成JSON的最大问题是输出不稳定——可能带markdown代码块、可能截断、可能有语法错误。本项目实现了四层容错解析
 

function parseItineraryJSON(raw) {

  if (!raw || typeof raw !== 'string') return null;


  // 策略1:直接解析(理想情况)

  try {

    const obj = JSON.parse(raw.trim());

    if (obj.nodes && Array.isArray(obj.nodes)) return obj;

  } catch (e) {}


  // 策略2:提取 ```json ... ``` 代码块

  const codeBlockMatch = raw.match(/```(?:json)?\s*([\s\S]*?)```/);

  if (codeBlockMatch) {

    try {

      const obj = JSON.parse(codeBlockMatch[1].trim());

      if (obj.nodes && Array.isArray(obj.nodes)) return obj;

    } catch (e) {}

  }


  // 策略3:找第一个 { 到最后一个 },并尝试修复

  const firstBrace = raw.indexOf('{');

  const lastBrace = raw.lastIndexOf('}');

  if (firstBrace !== -1 && lastBrace > firstBrace) {

    let jsonStr = raw.substring(firstBrace, lastBrace + 1);

    const repaired = repairJSON(jsonStr);  // 关键:自动修复

    if (repaired) {

      try {

        const obj = JSON.parse(repaired);

        if (obj.nodes && Array.isArray(obj.nodes)) return obj;

      } catch (e2) {}

    }

  }


  // 策略4:暴力提取nodes数组

  const nodesMatch = raw.match(/"nodes"\s*:\s*\[/);

  if (nodesMatch) {

    // ...尝试从nodes字段重建完整JSON

  }


  return null;

}


// 自动修复常见的AI输出JSON错误

function repairJSON(str) {

  let s = str;

  s = s.replace(/,\s*([}\]])/g, '$1');           // 去掉尾部逗号

  s = s.replace(/([{,]\s*)(\w+)\s*:/g, '$1"$2":'); // 补键名引号

  s = s.replace(/:\s*'([^']*)'/g, ':"$1"');       // 单引号→双引号

  

  // 补全缺失的闭合符号

  const openBraces = (s.match(/{/g) || []).length;

  const closeBraces = (s.match(/}/g) || []).length;

  for (let i = 0; i < openBraces - closeBraces; i++) s += '}';

  

  return s;

}

5.3 天气信息的融合

行程建议结合实时天气,提升实用性

async function getWeather(cityName) {
  const weatherUrl = `https://wttr.in/${encodeURIComponent(cityName)}?format=j1`;
  const response = await fetch(weatherUrl, { signal: AbortSignal.timeout(5000) });
  if (response.ok) {
    const data = await response.json();
    const cur = data.current_condition?.[0];
    return `当前:${cur.weatherDesc?.[0]?.value},${cur.temp_C}℃,体感${cur.FeelsLikeC}℃`;
  }
  return '(未获取到天气信息)';
}

6. 关键技术难点与解决方案

6.1 难点一:途径点与行程的双向同步

问题:用户可以在地图上手动添加途径点(waypoints[]),也可以让AI生成行程(itineraryData.nodes)。这两套数据之前互不感知——用户在地图上加了点,AI生成的行程里没有;AI生成的行程里的点,地图上的标记不对应。

解决方案——融合联动

// 在 addWaypointFromPOI 中(地图上点标记→添加途径点)

function addWaypointFromPOI(lat, lng, name) {

  waypoints.push({ name, lat, lng, stayDuration: 30 });

  renderWaypointPanel();

  

  // 【融合】同步到行程数据

  if (itineraryData && itineraryData.nodes) {

    itineraryData.nodes.splice(endIdx, 0, {

      type: 'waypoint', place: name, stayMinutes: 30, lat, lng

    });

    renderItineraryCards(itineraryData);

  }

}


// 在 removeItineraryNode 中(行程面板删除节点→同步途径点)

function removeItineraryNode(idx) {

  const node = itineraryData.nodes[idx];

  if (node.type === 'waypoint') {

    // 【融合】从 waypoints[] 中同步删除

    const wpIdx = waypoints.findIndex(w =>

      Math.abs(w.lat - node.lat) < 0.01 && Math.abs(w.lng - node.lng) < 0.01

    );

    if (wpIdx >= 0) waypoints.splice(wpIdx, 1);

    renderWaypointPanel();

  }

  itineraryData.nodes.splice(idx, 1);

  renderItineraryCards(itineraryData);

}

6.2 难点二:沿路线搜索POI的性能优化

问题:路线有几百个坐标点,对每个点做POI搜索是不可能的(API配额不够,也太慢)。

解决方案——抽样+距离过滤

async function searchPOIAlongRoute(from, to, keyword, routePath) {
  // 1. 对路线坐标抽样(每10个点取1个)
  const sampledPoints = routePath.filter((_, i) => i % 10 === 0);
  
  // 2. 并行搜索多个代表点(限制并发数)
  const results = [];
  for (const point of sampledPoints.slice(0, 5)) {
    const pois = await searchNearby(point, keyword, 2000);
    results.push(...pois);
  }
  
  // 3. 去重(同名POI只保留距离路线最近的)
  const unique = deduplicatePOIs(results);
  
  // 4. 计算每个POI到路线的距离(判断是否在沿途)
  for (const poi of unique) {
    poi._routeDist = minDistanceToRoute(poi.location, routePath);
  }
  
  return unique.filter(p => p._routeDist < 3000); // 3公里以内
}

 

6.3 难点三:单文件架构的代码组织

问题index.html是单文件应用(~3900行),HTML/CSS/JS全在一起,维护困难。

解决方案——逻辑分区+函数命名规范

/* ============ 分区1:地图与路线 ============ */

function initMap() { ... }

function planRoute() { ... }

function renderRoute() { ... }


/* ============ 分区2:途经点管理 ============ */

function addWaypoint() { ... }

function removeWaypoint() { ... }

function renderWaypointPanel() { ... }


/* ============ 分区3:AI行程规划 ============ */

function generateTravelStrategy() { ... }

function parseItineraryJSON() { ... }

function renderItineraryCards() { ... }


/* ============ 分区4:推荐系统 ============ */

function aiRecommendPOIs() { ... }

function renderRecommendPanel() { ... }

function confirmRecommendation() { ... }

7. 项目展示与Demo

ai旅途规划demo

7.1 功能演示流程

场景例:北京西站到故宫

第一步:输入起终点

在左侧边栏输入或与ai助手直接沟通

第二步:AI推荐途径点

第三步:查看AI生成的行程

【复制行程单】(示例):

📅 2026年5月8日 行程规划
🚦 出行方式:🚗 驾车
📝 建议合理安排行程,避免疲劳
☀️ 今天天气阴,气温适中,适合出行
────────────────────────────
08:00  🟢 出发  北京西站
       💡 从北京西站出发,天气阴,温度26℃
       → 驾车约34分钟
08:34  📍 途经  首都博物馆
       停留 60 分钟
       💡 首都博物馆是了解北京历史的好地方
       → 驾车约10分钟
09:44  📍 途经  什刹海公园
       停留 90 分钟
       💡 什刹海公园环境优美,适合散步
       → 驾车约15分钟
11:29  📍 途经  景山公园
       停留 90 分钟
       💡 景山公园可以俯瞰故宫全貌
       → 驾车约10分钟
13:09  📍 途经  前门大街景区
       停留 90 分钟
       💡 前门大街是体验北京传统文化的好去处
       → 驾车约15分钟
14:54  🔴 终点  故宫
       停留 120 分钟
       💡 故宫是北京的标志性景点,建议提前购票
────────────────────────────
🏁 预计 16:54 到达终点
由旅途规划 AI 助手生成

第四步:动态调整

  • 用户把故宫的停留时间改为90分钟 → 时间轴自动重算,后续节点时间整体偏移
  • 用户删除景山公园 → 路线自动重新规划(故宫直连颐和园)
  • 用户点"颐和园"卡片 → 地图飞到颐和园位置,弹出InfoWindow

8. 总结与展望

8.1 项目创新点总结

创新点

传统工具

本项目

行程生成

大段文字,不可交互

结构化JSON,可点击/修改/删除

途径点规划

用户自己想

AI沿路线推荐,可勾选

时间管理

静态,不改了

动态时间轴,改停留时长自动重算

地图联动

双向联动,点击即跳转

AI能力

无或仅聊天

深度集成,生成+推荐+对话三位一体

8.2 腾讯位置服务的价值体现

本项目中,腾讯位置服务提供了完整的位置能力栈

  • GLJS SDK:高性能WebGL地图,路线渲染流畅
  • 路线规划API:多模式(驾车/公交/步行/骑行)全覆盖
  • POI搜索API:丰富的地方数据库,沿途推荐有数据支撑
  • 坐标解析:地名→坐标,让AI生成的文字变成地图上的实实在在的标记

8.3 未来展望

阶段二:一句话生成完整攻略

当前版本需要用户手动输入起终点、选择途径点。未来的目标是真正的自然语言意图理解:

用户说:"去西湖玩一天,偏小众一点,下午要留时间喝茶。"

系统直接输出完整的可交互行程。

实现路径是在现有结构化行程 JSON 的基础上,引入对话式修改上下文——用户说"把第三个景点换成更冷门的",AI 识别意图、定位节点、就地修改,而不是推倒重来。整个行程对象始终保持结构化,支持用户自由拖拽调序、修改停留时长,最终一键"锁定方案"。

多日行程也是这一阶段的重要目标:AI 自动将景点按地理分区,合理规划每天的游玩范围,避免同一天在城市两端来回奔波。

阶段三:AI 预学习 + 随身导游(最终展望)

这是整个产品最令人期待的一跃。

行程确认之后、出发之前,存在一个宝贵的时间窗口。AI 利用这段时间主动学习目的地相关知识:景点历史典故、开放时间与门票规则、近期游客评价、节假日排队规律……用户出发时,AI 已经是一个"提前备课的本地通",而不是到了现场再临时现查。

真正到了旅途中,产品切换为随身导游模式

  • 位置感知触发讲解:用户走进故宫午门区域,AI 主动开口介绍历史背景,无需用户手动询问。
  • 随问随答:游览途中任何疑问,直接语音提问,AI 基于预学习的知识即时回答。
  • 行程动态调整:遇到排队过长或临时关闭,AI 实时感知并重排后续行程,推荐替代方案。
  • MCP 协议集成:将腾讯位置服务封装为 MCP Server,让 AI Agent 能够自主调用地图、搜索、路线等全部能力,真正实现"AI 自主驾驶"式的旅行辅助。

这三个阶段的演进逻辑是一脉相承的:结构化行程是地基,一句话规划是效率革命,AI 导游是体验升华。每一步都建立在上一步积累的数据结构和 AI 能力之上,而腾讯位置服务的 POI 数据、路线规划、实时位置 API 则是贯穿始终的核心基础设施。

作者注:本项目为原创作品,所有代码均由作者独立编写。腾讯位置服务API的使用符合其开放平台规范。AI生成内容已做工程化处理,不代表腾讯位置服务的官方建议。

如果觉得这篇文章对你有帮助,欢迎点赞、评论、转发!你们的支持是我继续优化的动力 。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐