尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

微信小程序路径规划实战:腾讯地图与高德地图双接入踩坑指南

微信小程序路径规划实战:腾讯地图与高德地图双接入踩坑指南 做小程序的人都知道位置服务几乎是避不开的模块。无论是外卖配送的路线预览、门店到店导航、物流轨迹回放还是同城服务的区域圈选背后都要靠地图和路径规划撑着。前阵子我接到一个需求在微信小程序里同时对接腾讯位置服务和高德地图实现从用户当前位置到目标地址的路径规划展示。这个需求听起来不算难但真正做起来有几个绕不开的坎Key怎么申请和绑定、SDK怎么接、两个地图服务的数据结构差异、坐标系的坑、路线渲染的细节。这篇文章就把我整个对接过程和踩坑记录整理出来给正在折腾地图服务的同行一些参考。这个项目最核心的就三块地图Key的申请与绑定、小程序SDK的接入、路径规划API的调用与路线绘制。我会把腾讯和高德两条线都走一遍从账号配置讲到真机调试尽量让看完的人能直接照着做。如果你只是想快速跑通一个Demo或者已经在线上被地图服务的某个报错卡住这篇内容应该都能帮上忙。1. 项目背景与方案选型1.1 为什么小程序的路径规划要单独对接很多刚入门的人会问小程序里不是有map组件吗直接拿来用不就行了这其实是个很大的误解。map组件只是提供了一个地图容器能展示底图、标记点、线路但它本身不会帮你算路线。路径规划需要从A点到B点算出一条可通行的路线这要依赖地图服务商的路网数据和路径计算引擎也就是通常说的路径规划API。路径规划是典型的高计算量服务底层涉及道路拓扑、交通流、通行限制等大量数据。小程序内置组件没这个能力各家地图服务商也不愿意把这么核心的能力免费白给而是通过Web API的形式开放给开发者。所以我们在小程序里做路径规划常规做法就是调起地图服务商的HTTP接口拿到路线坐标点数组再把这些点交给map组件的polyline画出来。还有一个更现实的原因不同业务方的地图供应商不一样。有的公司主体跟腾讯走得近有的项目一开始就用高德的地图数据做数据分析这时候小程序端就得跟着技术栈选同一家。我这次需求就是兼顾两边的所以腾讯和高德都要接。1.2 腾讯地图和高德地图怎么选做方案选型时我先列了一个对比维度把两个服务在小程序场景下的表现拉出来看了下。对比维度腾讯位置服务高德地图小程序SDK有官方 qqmap-wx-jssdk接口风格贴近小程序有官方 amap-wx接口完整但历史包袱略重Key绑定可绑定微信小程序AppID有效防跨端盗用应用类型可选“微信小程序”绑定AppID路径规划类型驾车、步行、骑行、公交驾车、步行、骑行、公交返回数据格式SDK自动处理部分编码REST接口polyline需解码polyline为字符串坐标序列需自行拆分免费配额个人认证有免费额度超出要付费个人开发者有日配额需关注账号实名等级文档体验文档分类清晰示例代码多文档全但部分接口更新后旧文章误导严重坐标系GCJ-02直接兼容微信地图组件GCJ-02直接兼容微信地图组件从实际开发感受来说如果你的项目纯粹是微信小程序场景腾讯位置服务会更顺手一点毕竟同属腾讯生态SDK的封装程度和文档的适配度明显更高。高德的优势在于数据积累和POI丰富度在偏业务型的地图服务比如门店搜索、周边生活服务上有优势。这次我的选择是小程序端以腾讯为主路径高德作为备份和对照方案同时验证两边API的兼容性。这样做的好处是万一某一家配额用尽或者临时故障可以直接切换另一家不至于业务瘫痪。1.3 需求拆解与功能清单对接之前我把需求拆成几个功能点获取用户当前定位通过wx.getLocation拿到经纬度。目的地输入与检索支持手动输入地址通过地理编码转成经纬度。路径规划调腾讯或高德的路径规划API拿到路线坐标。路线绘制在地图上用线把整条路画出来。起终点标记与信息展示起点、终点、全程距离、预计时间。多模式切换支持步行、驾车、骑行三种模式切换。这样的拆分逻辑其实是把小程序的UI层和地图服务的数据层解耦。UI层只管地图、标记、距离页面数据层只负责根据起终点经纬度和模式返回路线数据。后面不管是换地图商、加途经点还是加一个公交查询都只要改数据层。2. 开发前准备与Key申请2.1 腾讯位置服务Key申请流程腾讯位置服务的接入地址是lbs.qq.com先去注册账号然后进入控制台创建应用。创建应用的时候会让你填应用名称、应用类型记得选择“微信小程序”然后填小程序的AppID。这一步很关键如果类型选错或者AppID填错后面SDK调用会被拒。创建完应用后在应用详情里添加Key。腾讯位置服务的Key体系是应用下挂多个Key每个Key可以独立配置配额和域名白名单。我通常的做法是一个Key对应一个小程序环境比如正式环境一个、测试环境一个方便排查问题也不会因为某个环境调用量异常把整体配额打爆。拿到Key之后先别急着写代码。腾讯的Key在控制台可以配置安全设置包含小程序AppID绑定、Referer白名单等。虽然小程序端Key是必然暴露在代码里的但绑定了AppID后别人即使拿到Key也没法直接仿冒你的小程序去消耗配额这是最基本的防盗手段。这个环节不做好上线后Key被人拿去刷接口账单会很难看。2.2 高德地图Key申请流程高德的接入位置是console.amap.com同样先注册开发者账号。这里有个要踩的坑高德的账号分个人和企业个人开发者实名认证后部分接口的配额会比较低路径规划的日调用量上限和企业版差距明显。如果只是开发测试个人版够用但如果准备商用上线强烈建议提前申请企业认证不然后面还要二次改造。创建应用时选择服务平台为“微信小程序”然后系统会生成一个Key。高德的Key体系里每个应用会有一个独立的Key同一个应用下面还可以开通不同的API服务。我们在控制台把需要的服务开启即可比如“路径规划API”“地理编码API”。注意高德在2021年之后升级了API Key的安全策略部分新接口要求配合安全密钥jscode使用。小程序SDK老版本不需要但如果直接用REST接口就会碰到建议以官方最新文档为准。高德那边有一个让我印象很深的细节Key的配额是按“每日”计算的而且有些免费配额是按“每日压测量”算突然的大流量绝对会触发限流。后面我在线上遇到过一天中某个小时接口突然全部失败的情况排查了半天发现是单小时并发触发了限流这个后面单独说。2.3 微信小程序后台域名配置这是第一次接地图服务的人都容易忽略的环节。小程序运行的时候API请求必须指向后台配置过的合法域名否则真机上会直接报request:fail url not in domain list。腾讯位置服务要配的域名是https://apis.map.qq.com高德要配的是https://restapi.amap.com。在小程序后台的“开发管理-服务器域名”里把对应域名加到request合法域名列表里即可。开发阶段可以在开发者工具里勾选“不校验合法域名”这样本地调试不会被拦截。但注意这只是权宜之计上线之前一定记得把域名配上并且在小程序后台把HTTPS证书检查开起来。有些人开发时用着没事上线后却发现地图全挂十有八九就是漏了这一步。2.4 小程序的定位权限准备路径规划的起点通常是用户当前定位所以还要在app.json或页面配置里声明定位权限。微信小程序的定位接口是wx.getLocation使用时需要在app.json的permission节点里说明用途{ permission: { scope.userLocation: { desc: 你的位置信息将用于路径规划导航 } } }这里的desc必须写得明确微信审核时如果发现用途描述与实际功能不符会被驳回。有些项目还涉及后台持续定位那就需要申请scope.userLocationBackground审核更严格这次用不到就不展开了。调用wx.getLocation的时候参数里有一个type字段强烈建议设置为gcj02。这里就牵出地图开发里最重要也最容易踩坑的概念——坐标系。3. 坐标系与SDK接入3.1 坐标系很多人栽在这里国内所有地图服务商对外输出的经纬度基本都是GCJ-02坐标系也就是俗称的“火星坐标系”。而手机GPS芯片原始返回的是WGS-84坐标系也就是国际通用的标准经纬度。这两个坐标系之间存在偏移通常会有几十米到几百米的差距直接把GPS原始坐标怼到地图上标记点位置就会偏到隔壁街甚至隔壁小区。微信的wx.getLocation很贴心地支持直接返回GCJ-02坐标所以我在代码里固定用wx.getLocation({ type: gcj02, success: (res) { // res.latitude / res.longitude 已经可用于地图组件 } });腾讯和高德的路径规划API接收的起终点坐标也都是GCJ-02所以只要你统一用wx.getLocation的 GCJ-02 结果这条链路就不会出问题。真正会出问题的场景是某些业务是从后台接口拿GPS数据或者从第三方平台取了WGS-84坐标直接传路径规划API算出来的路线起点会偏离。判断坐标是不是GCJ-02最简单的方式拿坐标到腾讯或高德的地图页面上反查位置如果地图上标的点和实际地点对不上大概率就是WGS-84混进来的数据。这种情况要么在服务端做坐标转换要么在前端用现成的转换算法处理。腾讯和高德都没直接提供在线的坐标转换API前端的转换算法网上很多我就不贴大段代码了但提醒一句每次转换都有精度损耗能拿到GCJ-02就尽量别转。3.2 腾讯地图小程序SDK接入腾讯位置服务给小程序开发者提供了一个专门的SDK叫做qqmap-wx-jssdk。下载下来之后把文件放进小程序的libs目录然后在页面或工具类里引用const QQMapWX require(../../libs/qqmap-wx-jssdk.js); const qqmapsdk new QQMapWX({ key: 你的腾讯位置服务Key });这个SDK封装了地理编码、逆地理编码、路径规划、搜索等常用接口。路径规划对应的方法是direction后面我会专门讲。接入时有几个版本上的坑要注意。这个SDK的更新频率不算高有些网上下载的是几年前的版本接口行为跟当前腾讯WebService API不完全一致。我的建议是去腾讯位置服务的官网文档页下载最新版别从博客或下载站找。还有无论SDK版本怎么变Key的读取方式都是一样的初始化时传进去即可。3.3 高德地图小程序SDK接入高德给小程序用的SDK是amap-wx.js同样放在libs目录下引用const amap new AMapWX({ key: 你的高德Key });高德SDK的路径规划方法是getRoute。它的设计偏向于把REST API简单封装了一下底层的参数和返回结构和腾讯风格不同这个耐心看文档就好。高德SDK有一个让人难受的点官方文档里的示例代码有时候和老SDK版本不匹配。我发现网上很多教程贴的代码是用amapFile加载方式而新版已经改成了AMapWX直接实例化。抄别人的代码之前一定先对着官方文档过一遍方法名。我在这里也踩过坑照着旧文章跑出来的代码各种报Undefined is not a function浪费了一个下午。3.4 组件模式、JS API模式和REST模式的区别在小程序里使用地图服务其实有三条路线组件模式直接用微信的map组件纯展示地图和覆盖物不定制点线面。SDK模式用腾讯或高德的小程序SDK调用Web API拿到数据后再交给map组件绘制。REST模式自己用wx.request直接请求地图服务商HTTP接口完全掌控数据链路。SDK模式是我们这次主要用的因为它省去了自己拼URL、处理签名、解析JSON的重复劳动。但我不建议把SDK当成黑盒因为SDK内部也在发普通的HTTP请求最终数据格式还是跟REST API一致的。如果遇到SDK里解决不了的问题比如某个新功能SDK没封装直接切到REST模式反而更快。4. 路径规划API调用实战4.1 路径规划API首先搞清楚一件事调用路径规划之前先想明白一件事你要算的是两点之间的“直线”还是“可行路线”很多人会把测距工具和路径规划混在一起。腾讯和高德都提供测距接口那个算出来的是球面距离纯数学计算路径规划则是基于路网数据算出来的会考虑道路走向、单行道、禁止转弯等因素。两者的结果差异很大用途也不同。我这次是用在小程序的到店导航预览上必须走路径规划接口。路径规划接口本质上是一类“交通网络计算”任务服务端要做的是根据起点、终点坐标结合当前路网的拓扑关系搜索出一条满足通行规则的路线。所以它的核心参数就是三个起点坐标、终点坐标、出行方式。有些接口还支持途经点、避让区域、路线偏好等高级参数先用基础参数跑通再慢慢加。4.2 步行路径规划的实现步行路线是最简单的不涉及道路单向限制只走人行道路网。腾讯的direction方法写法如下qqmapsdk.direction({ mode: walking, from: ${startLat},${startLng}, to: ${endLat},${endLng}, success: (res) { // 从 res.result.routes 拿路线数据 }, fail: (err) { console.error(路线规划失败, err); } });高德对应的getRoute写法amap.getRoute({ mode: walking, origin: ${startLng},${startLat}, destination: ${endLng},${endLat}, success: (res) { // 从 res.routes 拿路线数据 }, fail: (err) { console.error(路线规划失败, err); } });注意一个关键差异腾讯的from和to顺序是纬度,经度高德的origin和destination顺序是经度,纬度。这个顺序搞反了接口不会报错但你会发现自己规划的路线起点在另一个城市或者路线直接画歪了。我第一次对接高德的时候就栽在这个地方排查了半天才发现是经纬度顺序问题。高德返回的路线数据里res.routes[0].steps是一系列“路段”step每个step有自己的polyline字段是一个字符串。这个字符串形如116.397428,39.90923;116.397428,39.90923;116.397489,39.909058需要自己按分号和逗号拆分成坐标点数组。腾讯的SDK在新版本里已经帮我们把polyline解析成数组了但如果直接调REST接口拿到的还是一串需要解码的编码字符串。腾讯用的编码方式和Google的Encoded Polyline Algorithm相同网上有现成的解码JS实现。开发时我在工具函数里把两种数据源统一处理成相同结构这样上层渲染逻辑就不用关心底层是腾讯还是高德。4.3 驾车路径规划的实现与参数细节驾车路线比步行复杂因为要考虑道路方向、限行、拥堵等因素。腾讯的驾车模式是mode: driving直接传起终点坐标。高德的驾车模式是mode: driving但在getRoute里需要额外的city参数代表终点城市名字或城市编码。这个参数在文档里标的是“可选”但实测如果漏了有些城市会返回规划失败或者绕远路。我的经验是尽量传上不确定城市编码就传中文城市名比如city: 北京市。如果你要支持“多条路线备选”腾讯返回的routes里可能包含多条路线SDK默认只返回一条主路线。高德的getRoute不直接提供多方案需要走REST接口并传strategy参数才能拿到多条。做“路线对比”功能的话我建议两边都走REST模式因为SDK对多方案的支持比较有限。我实际接线上项目时驾车路线还会涉及一个“路线偏好”的问题。比如有的用户走高速优先有的用户避免拥堵。腾讯的 direction 里可以传policy高德可以传strategy。参数值各家不同每次对接时查一下最新文档我不建议背参数值因为服务商会不定时调整枚举值。4.4 骑行路径规划的实现骑行和步行很像但路网数据不同。骑行走的是自行车道很多骑行道是单向或禁行自行车的所以结果跟步行路线不完全一致。腾讯骑行模式是mode: bicycling高德也是mode: bicycling调用方式跟步行几乎一样。我在开发骑行功能时发现一个有意思的现象有时候骑行的预计时间比步行还长。原因是骑行路线会绕开部分步行快速通道比如天桥、地下通道反而兜圈子。如果产品上只展示“预计时间”这种反直觉的结果会被用户吐槽最好在UI文案上标注“骑行路线根据自行车道规划”。4.5 统一封装避免业务代码重复既然要同时兼容腾讯和高德我在项目里做了一个统一的路线服务封装。对外暴露方法时只关心三个输入起点经纬度终点经纬度出行模式内部再去判断当前用的是腾讯还是高德拼参数、发请求、解析结果。这样页面层完全不用关心地图服务商是谁。封装之后的调用大概是这种感觉async function getRouteWithMode(provider, mode, start, end) { if (provider tencent) { return requestTencentRoute(mode, start, end); } if (provider amap) { return requestAmapRoute(mode, start, end); } throw new Error(不支持的路线服务商); }每家返回的路线数据结构不一样建议在解析层统一输出成这样的对象{ distance: 1500, duration: 1200, points: [ { latitude: 39.90923, longitude: 116.397428 } ] }distance的单位是米duration的单位是秒。这样后面无论是地图渲染、距离展示、时间格式化全都基于一套数据结构省掉大量if else。5. 地图渲染与路径展示5.1 用map组件把地图铺起来路线数据拿到之后就是展示环节。微信小程序的地图展示直接用map组件map idrouteMap stylewidth: 100%; height: 400px; latitude{{centerLat}} longitude{{centerLng}} markers{{markers}} polyline{{polyline}} include-points{{includePoints}} /latitude和longitude控制地图中心markers用来显示起点和终点标记polyline用来画路线include-points可以自动调整视野把所有点装进屏幕。这里要注意一个渲染顺序的问题一开始先拿到定位把地图中心设在起点附近再请求路径规划拿到路线后更新polyline和includePoints。如果一次性想设置多个数据建议用setData一起更新减少渲染次数。5.2 polyline画路线的正确姿势polyline是map组件中用数组方式配置的一组线。一个路线对象大概长这样{ points: [ { latitude: 39.90923, longitude: 116.397428 } ], color: #1A7FFF, width: 6, arrowLine: true }这里最容易犯的错就是把腾讯或高德返回的原始字符串直接塞给points。组件要的是对象数组不是字符串。我之前在代码里就出现过polyline: [{ points: res.routes[0].steps.map(step step.polyline) }]这样写看起来没什么问题但如果是高德的数据源step.polyline是一串字符串整条线路就变成了一段抽风的折线。必须在解析层就把字符串拆成坐标数组并拼接成一条完整的点序列。上面这个坑还引申出另一个问题路线的点数太多setData的数据量大页面渲染会卡。一条城市级的驾车路线坐标点可能有上千个不加处理直接渲染低端手机会明显掉帧。我的处理方案是“抽稀”——每隔几个点取一个保持路线形状的同时大幅减少数据量。对于折线型路线抽稀对视觉效果影响很小但对性能帮助很大。5.3 起终点标记和气泡信息起点和终点分别加一个marker体验会好很多。markers的配置形如{ id: 0, latitude: 39.90923, longitude: 116.397428, title: 起点, iconPath: /assets/start.png, width: 30, height: 30 }iconPath支持项目内的本地图片路径建议直接用PNG格式的小图尺寸控制在32像素以内太大会挡住路线。官方文档里说大尺寸图标在某些机型上会出现锯齿实测确实如此。如果你还想展示距离和时间可以在地图下方放一个卡片卡片数据就是从封装层统一输出的distance和duration。这里有一个体验细节距离大于1公里时用公里小于1公里时用米时间超过60分钟显示“X小时X分钟”。这些格式化逻辑很琐碎但做好的话产品质感会明显提升。5.4 把视野调整到能看到整条路线路线画好之后地图视野要能容纳整条路线。实现方式有两种组件上直接传include-points{{includePoints}}把所有路线坐标点放进去。通过MapContext.includePoints动态调整。用MapContext的时候要先通过wx.createMapContext(routeMap, this)拿到上下文实例然后调用mapContext.includePoints({ points: routePoints, padding: [35, 35, 35, 35], success: () {} });padding是用来控制路线距离屏幕边缘的留白单位是像素建议设置至少15像素以上不然路线会贴边看起来憋屈。这里还有一个注意点如果路线点数太多把整条路线的所有点都传给includePoints部分安卓机型会出现缩放不平滑甚至白屏。优化办法是先抽稀再传给includePoints。6. 高频问题与排坑实录6.1 一张表搞定常见问题我把这段时间碰到的问题整理成了一张速查表方便排查。问题现象常见原因解决方向地图白屏/灰屏Key未配置、域名未添加、组件高度为0检查Key和AppID绑定、后台域名、页面样式高度路线规划回调失败Key类型不对、配额耗尽、参数经纬度顺序错检查Key绑定类型、控制台配额、照着文档调参数标注点偏了用了WGS-84坐标统一用GCJ-02wx.getLocation指定type路线画出来乱飞polyline点数据没解成坐标数组按服务商返回格式拆分坐标再渲染真机好用但开发者工具有问题开发者工具与真机内核不一致以真机为准工具主要用于调试样式高德某段时间全部失败触发单小时并发限流看配额用量、做降级切换腾讯腾讯路线没有步骤详情版本旧或接口配额限制升级SDK查看新版返回结构表格没法写太细我挑几个重要的展开说。6.2 注意Key的防盗与配额管理小程序端的Key是无法真正隐藏的任何前端代码反编译后都能拿到。所以我能做的是外部防盗和内部限流两头堵。外部防盗就是前面说的绑定AppID别人拿你的Key去请求接口因为AppID对不上会被拒绝如果服务商支持“IP白名单”还可以再加一道限制。内部限流是在自己的后端加一层调用计数比如每个用户每天最多规划20次超了就走缓存或降级方案。还有一个很少被人提的点Key的配额是按“服务维度”分别计算的。路径规划和你用的地理编码、逆地理编码各自消耗各自的额度。很多项目路径规划没超量但地理编码超了整个服务商接口可能在短时间内都无法访问。建议在后台给每个服务分别设置配额告警超额的第一时间收到短信或邮件通知别等到用户反馈才去查。6.3 线上真机调试与性能优化真机调试时我习惯在开发者工具里打开Network面板专门看一下地图服务请求的耗时、返回大小。有一次我发现腾讯的路径规划响应只有几十KB但高德返回了近300KB原因是高德会把每一步的详细指引文本、图标信息一并返回而我只想画个线。这种冗余数据在小屏设备上会影响解析和渲染速度。我的优化策略是不需要路段指引文字时直接只提取polyline坐标点。对路线点做抽稀减少setData数据量。避免频繁调用路径规划用户拖动地图时不要实时重算只在起点、终点变化后重算。加一层简单的内存缓存相同起终点和模式的路线在短时间内不重复请求。路径规划服务本身是耗资源的一个小程序如果用户量大一天几万次调用很正常做好缓存对配额和用户体感都重要。6.4 线上应急切换的心得我在项目里做了一个小开关可以在远端配置当前地图服务商是“腾讯”还是“高德”。这个开关平时不常用但真到腾讯某个时段接口限流或者高德配额告急时后端只需要改一个配置小程序端下次请求就会全部切到另一家。最开始我还没做这个开关的时候线上出过一次问题某天下午路线规划接口开始大面积超时用户反馈导航用不了。因为当时代码写死了只用高德我只能发版修复整整折腾了大半天。后来加了动态配置和失败自动降级再遇到类似问题就从容多了。这种“双服务商保底”的思路本质上是用冗余换可用性。对于位置服务这种高度依赖第三方API的业务我强烈建议有条件的话都做一版。结个尾我踩过这些坑之后的体会我已经有一个多月没碰这个项目了但回头再看最想分享的不是某个函数怎么调而是“地图服务对接本质上是数据对接不是UI对接”这个认知。腾讯和高德虽然做的都是地图生意但接口参数、返回结构、坐标系处理、配额规则各有各的脾气。你花在调试上的大量时间往往不是地图画不出来而是腾讯要纬度在前高德要经度在前腾讯SDK已经把polyline解好了高德还给你留一串字符串腾讯的骑行路线绕天桥高德的步行路线穿公园。这些差异才是真正决定开发体验的东西。如果让我给一个新项目提建议我会说先用腾讯位置服务跑通整个Demo因为它跟微信小程序的契合度确实更高然后花一天时间把高德的getRoute也跑通做一个切换开关放后端兜底最后把Key、域名、坐标、polyline这四个最容易出问题的环节写进团队代码规范里别让后面接手的人再踩一遍。做小程序地图就是一场细心活希望你少走点弯路。
返回列表