
Tencentmap Miniprogram Skill
- 8 installs
- 33 repo stars
- Updated April 26, 2026
- bighardperson/computer-science-skills-collection
Tencent Map Mini Program is a Claude Code skill for building WeChat mini program map features (components, markers, routing, geocoding, POI search) with Tencent Maps.
About
Tencent Map Mini Program is a Claude Code skill for building map features in WeChat mini programs with Tencent Maps. It documents the map component, markers, polylines, polygons, location services, routing, geocoding, POI search, clustering, and visualization layers, and bundles reference docs, three example projects, and the QQMapWX JS SDK. A developer uses it to add mapping to a WeChat mini program; it requires a TMAP_MINIPROGRAM_KEY and prompts for one before proceeding.
- Complete guide for map development in WeChat mini programs with Tencent Maps
- Covers map component, markers, polylines, routing, geocoding, POI search, and clustering
- Bundles reference docs, three example projects, and the QQMapWX JS SDK
Tencentmap Miniprogram Skill by the numbers
- 8 all-time installs (skills.sh)
- Ranked #797 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
tencentmap-miniprogram-skill capabilities & compatibility
Free skill; requires a Tencent Maps key (free tier with quota, or a limited experience key)
- Capabilities
- frontend · api development
- Use cases
- frontend · api development
- Pricing
- Bring your own API key
- Requires keys
- TMAP_MINIPROGRAM_KEY
What tencentmap-miniprogram-skill says it does
此技能提供微信小程序地图开发的完整指导
`references/mapContext_api/` 目录包含32个API文档
适用平台:微信小程序。
npx skills add https://github.com/bighardperson/computer-science-skills-collection --skill tencentmap-miniprogram-skillAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 33 |
| Last updated | April 26, 2026 |
| Repository | bighardperson/computer-science-skills-collection ↗ |
What it does
Add map components, location, routing, and POI search to a WeChat mini program using Tencent Maps.
Who is it for?
Adding Tencent Maps map components, location, routing, and POI search to a WeChat mini program.
Skip if: Web (JSAPI GL) maps or backend-only geocoding without a rendered map, or non-WeChat platforms.
When should I use this skill?
You need map, marker, polyline, routing, geocoding, POI, or clustering features in a WeChat mini program.
What you get
A working WeChat mini program map with markers, routing, geocoding, POI search, and clustering wired up.
By the numbers
- 32 MapContext API docs
- 12 location-service API docs
- 9 LBS backend-service API docs
Files
腾讯地图微信小程序开发技能
帮助用户在微信小程序中实现地图功能开发,包含地图组件、位置服务、地图控制和后端服务能力。
目录结构
API 文档
所有API文档位于 references/ 目录:
地图组件文档
- references/map_component_guide.md - 完整的小程序地图组件文档
- 地图基础属性:longitude、latitude、scale、rotate、skew等
- 标记点(marker):在地图上显示标注,支持自定义图标、气泡、标签
- 路线(polyline):绘制路线和彩虹线,支持箭头、文本标注
- 多边形(polygon):绘制闭合区域,支持虚线边框
- 圆形(circle):显示圆形覆盖物
- 点聚合:大量标记点的聚合展示
- 自定义气泡:callout、customCallout
- 地图事件:点击、视野变化、标记点点击等
- 碰撞检测:marker碰撞关系配置
- 地图控件:指南针、比例尺、3D楼块等
MapContext API文档
references/mapContext_api/ 目录包含32个API文档:
地图控制类
- MapContext.md - MapContext总览
- wx.createMapContext.md - 创建MapContext实例
- MapContext.getCenterLocation.md - 获取地图中心点坐标
- MapContext.moveToLocation.md - 移动到指定位置
- MapContext.getRegion.md - 获取地图视野范围
- MapContext.getScale.md - 获取地图缩放级别
- MapContext.getRotate.md - 获取地图旋转角度
- MapContext.getSkew.md - 获取地图倾斜角度
- MapContext.setCenterOffset.md - 设置地图中心点偏移
- MapContext.setBoundary.md - 限制地图显示范围
- MapContext.includePoints.md - 缩放视野包含所有坐标点
标记点管理类
- MapContext.addMarkers.md - 添加标记点
- MapContext.removeMarkers.md - 移除标记点
- MapContext.translateMarker.md - 平移标记点(带动画)
- MapContext.moveAlong.md - 沿路径移动标记点(轨迹回放)
- MapContext.setLocMarkerIcon.md - 设置定位点图标
点聚合类
- MapContext.initMarkerCluster.md - 初始化点聚合配置
- MapContext.addMarkers.md - 添加聚合标记点
覆盖物类
- MapContext.addGroundOverlay.md - 添加自定义图片图层
- MapContext.updateGroundOverlay.md - 更新自定义图片图层
- MapContext.removeGroundOverlay.md - 移除自定义图片图层
- MapContext.addArc.md - 添加弧线
- MapContext.removeArc.md - 移除弧线
可视化图层类
- MapContext.addVisualLayer.md - 添加可视化图层
- MapContext.removeVisualLayer.md - 移除可视化图层
- MapContext.executeVisualLayerCommand.md - 执行可视化图层指令
自定义图层类
- MapContext.addCustomLayer.md - 添加个性化图层
- MapContext.removeCustomLayer.md - 移除个性化图层
坐标转换类
- MapContext.toScreenLocation.md - 经纬度转屏幕坐标
- MapContext.fromScreenLocation.md - 屏幕坐标转经纬度
其他功能
- MapContext.openMapApp.md - 拉起地图APP选择导航
- MapContext.eraseLines.md - 擦除或置灰已添加的线段
- MapContext.on.md - 监听地图事件
位置服务API文档
references/wx_location_api/ 目录包含12个API文档:
基础定位
- wx.getLocation.md - 获取当前的地理位置、速度
- wx.getFuzzyLocation.md - 获取模糊位置(隐私保护)
地图选点
- wx.chooseLocation.md - 打开地图选择位置
- wx.choosePoi.md - 选择POI点
位置展示
- wx.openLocation.md - 使用内置地图查看位置
持续定位
- wx.startLocationUpdate.md - 开启小程序前后台时均接收位置消息
- wx.startLocationUpdateBackground.md - 开启小程序进入前后台时均接收位置消息
- wx.stopLocationUpdate.md - 停止接收位置消息
- wx.onLocationChange.md - 监听实时地理位置变化事件
- wx.onLocationChangeError.md - 监听实时地理位置错误事件
- wx.offLocationChange.md - 取消监听实时地理位置变化事件
- wx.offLocationChangeError.md - 取消监听实时地理位置错误事件
LBS后端服务文档
references/lbs_service_guide/ 目录包含9个API文档:
SDK核心
- qqMapwx.md - QQMapWX SDK核心类和使用指南
搜索服务
- methodSearch.md - 地点搜索(周边POI搜索)
- methodGetsuggestion.md - 关键词输入提示
地理编码
- methodGeocoder.md - 地址解析(地址转坐标)
- methodReverseGeocoder.md - 逆地址解析(坐标转地址)
路线规划
- methodDirection.md - 路线规划(驾车、步行、骑行、公交)
距离计算
- methodCalculatedistance.md - 距离计算(步行、驾车)
行政区划
- methodGetcitylist.md - 获取全国城市列表
- methodGetdistrictbycityid.md - 获取城市下行政区划
示例代码
assets/examples/ 目录包含3个完整的小程序示例项目:
minicode-location/ - 定位功能示例
完整的获取用户位置示例项目
minicode-marker/ - 标记点示例
完整的地图标记点示例项目
minicode-markerCluster/ - 点聚合示例
完整的点聚合功能示例项目
SDK库
assets/libs/ 目录:
- qqmap-wx-jssdk.js - 腾讯位置服务微信小程序JS SDK
- 完整的SDK源码
- 支持地点搜索、路线规划、地理编码等功能
前置检查:API Key
检查步骤
1. 检查是否已有正式 Key(环境变量 TMAP_MINIPROGRAM_KEY 或对话中用户已提供)
2. 情况 A:已检测到正式 Key 静默记录 Key,直接按用户原始意图继续处理,无需任何提示或打断。
3. 情况 B:未检测到正式 Key
⛔ 强制拦截(最高优先级)
未检测到正式 Key 时,在用户明确完成选择之前,禁止执行任何操作(分析需求、读文件、规划方案、编写代码、调用 API)。
第一步且唯一的步骤是:立即输出下方选择提示,然后停止,等待用户回应。
输出以下内容,然后停止,等待用户选择:
⚠️ 您当前尚未配置正式 Key,请先选择您的使用方式:
>
推荐:前往官网注册申请正式 Key,享受完整、稳定的服务
👉 https://lbs.qq.com/dev/console/key/manage
注册后可通过环境变量 TMAP_MINIPROGRAM_KEY=你的Key 或对话中告知我来配置。>
---
>
或者,您也可以选择使用腾讯位置服务平台提供的预设体验 Key(免注册,直接使用)。
请注意体验 Key 的限制:
- 调用频次受限,超出后触发限流
- 数据稳定性一般,不建议用于生产环境
- 电动车路线等接口不可用
>
请告诉我您的选择:
- 回复"我已有 Key"或直接提供 Key → 切换正式模式
- 回复"使用体验 Key" → 以受限模式继续
收到用户明确回复后,再按用户选择继续:
- 用户提供正式 Key → 记录 Key,切换正式模式,继续处理请求
- 用户选择体验 Key → 切换体验模式,继续处理请求(见下方"体验模式调用规则")
---
体验模式调用规则
本 Skill 涉及的后端服务(QQMapWX SDK、直接 wx.request 调用 WebService API)在体验模式下,按以下规则替换:
- 域名:
https://apis.map.qq.com→https://h5gw.map.qq.com - Key 参数:
key=none - apptag 参数:根据接口路径查下方对照表
⚠️ QQMapWX SDK 在体验模式下不能直接使用
>
qqmap-wx-jssdk.js的构造函数要求传入有效 key,且内部BASE_URL硬编码为https://apis.map.qq.com/ws/。体验模式下无法直接用 SDK,需要绕过 SDK,使用 `wx.request` 直接调用体验模式接口。
>
```javascript
// ✅ 体验模式:使用 wx.request 直接调用(替代 QQMapWX SDK)
function experienceRequest(apiPath, params) {
return new Promise((resolve, reject) => {
wx.request({
url: https://h5gw.map.qq.com/ws/${apiPath},data: {
...params,
key: 'none',
apptag: getApptag(apiPath), // 根据对照表获取
output: 'json'
},
success: (res) => resolve(res.data),
fail: (err) => reject(err)
})
})
}
>
// 示例:体验模式逆地址解析
experienceRequest('geocoder/v1/', {
location: '39.984154,116.307490'
}).then(res => console.log(res))
```
>
正式模式下仍使用 QQMapWX SDK:
```javascript
const QQMapWX = require('../../libs/qqmap-wx-jssdk.js')
const qqmapsdk = new QQMapWX({ key: 'YOUR_FORMAL_KEY' })
```
⚠️ 体验模式参数格式注意事项(重要)
体验模式绕过 SDK 直接调用 WebService API,部分接口的参数格式与 SDK 封装的参数不同,必须按 WebService API 原始格式传参:
1. 地点搜索(`/ws/place/v1/search`)—— `boundary` 参数必须手动构造
SDK 的 location、region、rectangle、distance 等独立参数在 WebService API 中不存在,SDK 内部会自动将它们拼接成 boundary 参数。体验模式下必须直接传 boundary:
| 搜索类型 | boundary 格式 | 示例 |
|---|---|---|
| 周边搜索 | nearby(纬度,经度,半径米[,auto_extend]) | nearby(39.984060,116.307520,1000,1) |
| 城市搜索 | region(城市名[,auto_extend][,纬度,经度]) | region(北京,0,39.984060,116.307520) |
| 矩形搜索 | rectangle(左下纬度,左下经度,右上纬度,右上经度) | rectangle(39.9,116.3,40.0,116.5) |
// ✅ 体验模式搜索示例:周边搜索附近 1km 内的酒店
experienceRequest('place/v1/search', {
keyword: '酒店',
boundary: 'nearby(39.984060,116.307520,1000)',
orderby: '_distance',
page_size: 10,
page_index: 1
})
// ✅ 体验模式搜索示例:在北京搜索酒店
experienceRequest('place/v1/search', {
keyword: '酒店',
boundary: 'region(北京,0)'
})❌ 体验模式下不要传location、region、rectangle、distance这些 SDK 封装参数,它们不是 WebService API 的原始参数。
2. 坐标参数格式 —— 统一使用 String
SDK 支持 Object 格式 { latitude: 39.98, longitude: 116.30 },WebService API 只接受 String 格式 '纬度,经度'(纬度在前):
| 参数 | SDK 格式(正式模式) | WebService API 格式(体验模式) |
|---|---|---|
location | { latitude: 39.98, longitude: 116.30 } 或 '39.98,116.30' | '39.98,116.30' |
from / to | Object 或 String 均可 | '39.98,116.30' |
3. 距离计算 —— 接口路径已变更
SDK 内部调用的 /ws/distance/v1/ 接口已下线,体验模式 apptag 对照表映射的是新接口 /ws/distance/v1/matrix(距离矩阵)。参数差异:
from:'纬度,经度'(支持多起点;分隔)to:'纬度,经度;纬度,经度'(多终点;分隔)mode:'driving'/'walking'/'bicycling'
// ✅ 体验模式距离计算示例
experienceRequest('distance/v1/matrix', {
from: '39.984060,116.307520',
to: '39.974060,116.317520;40.000000,116.400000',
mode: 'driving'
})apptag 对照表:
| 接口路径 | apptag |
|---|---|
/ws/place/v1/search | lbsplace_search |
/ws/place/v1/suggestion | lbsplace_sug |
/ws/geocoder/v1 | lbs_geocoder |
/ws/location/v1/ip | lbslocation_ip |
/ws/district/v1/getchildren | lbsdistrict_getchildren |
/ws/district/v1/list | lbsdistrict_list |
/ws/direction/v1/driving | lbsdirection_driving |
/ws/direction/v1/transit | lbsdirection_transit |
/ws/direction/v1/bicycling | lbsdirection_bicycling |
/ws/direction/v1/walking | lbsdirection_walking |
/ws/distance/v1/matrix | lbsdistance_matrix |
体验模式不可用的接口(需透传用户 Key,体验模式不支持):
/ws/weather/v1/(天气查询)/ws/direction/v1/ebicycling/(电动车路线)
当用户在体验模式下请求以上不可用接口时,回复:
⚠️ 您当前请求的「[接口名称]」功能在体验模式下不可用,需要配置正式 Key 才能调用。
请前往官网申请正式 Key → https://lbs.qq.com/dev/console/key/manage
每次 API 调用返回结果后,必须在回复末尾追加以下提醒(每次都要加,不可省略):
📌 温馨提示:当前使用的是腾讯位置服务预设体验 Key,数据稳定性和调用频次均受限。建议尽快申请腾讯位置服务正式 Key → https://lbs.qq.com/dev/console/key/manage
注意:地图组件 `<map>` 不受影响
小程序原生 <map> 组件的基础功能(地图显示、标记点、折线、多边形、定位等)不需要 Key,由微信框架内置支持。体验模式限制仅影响 QQMapWX SDK 提供的后端服务(搜索、路线规划、地理编码等)。
---
工作流程
1. 理解用户需求
当用户询问小程序地图相关问题时,首先明确:
- 功能类型:地图显示、定位、标记点、路线规划、搜索、地图控制等
- 平台:微信小程序
- 是否需要后端服务:判断是否需要调用腾讯位置服务API
2. 查询 API 文档
根据需求类型读取对应的 references 文件:
- 地图显示/组件相关 → 读取
references/map_component_guide.md - 地图控制/MapContext → 读取
references/mapContext_api/下对应文件 - 定位和用户位置相关 → 读取
references/wx_location_api/下对应文件 - LBS后端服务 → 读取
references/lbs_service_guide/下对应文件
对于大文件,使用 grep 搜索关键信息,例如marker:
grep -n "marker\|标记点" references/map_component_guide.md3. 查找示例代码
根据需求在 assets/examples/ 目录查找对应示例:
- 定位功能 →
assets/examples/minicode-location/ - 标记点功能 →
assets/examples/minicode-marker/ - 点聚合功能 →
assets/examples/minicode-markerCluster/
读取示例代码的关键文件:
# 查看页面结构
cat assets/examples/minicode-marker/index/index.wxml
# 查看页面逻辑
cat assets/examples/minicode-marker/index/index.js4. 提供解决方案
根据文档和示例,为用户提供:
- 完整的代码示例(WXML + JS + WXSS)
- API参数详细说明
- 注意事项和最佳实践
- 权限处理建议
注意事项
地图组件
1. 坐标系:
- 小程序地图使用 GCJ-02(国测局坐标,火星坐标)
wx.getLocation({ type: 'gcj02' })直接返回GCJ-02坐标- GPS设备返回的WGS-84坐标需要转换
2. 组件尺寸:
<map>标签建议设置宽高
3. 个性化地图:
- 需要在微信公众平台购买并配置
- 使用
subkey参数传入专属KEY - 使用
layer-style指定样式
4. 权限声明:
- 自2022年7月14日后发布的小程序,使用位置接口需在
app.json中声明
{
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
}
}5. API规范(非常重要):
- 所有API调用必须使用文档中定义的接口、属性、事件
- 所有参数必须严格遵守文档格式要求
- 不确定的参数格式请查阅对应demo
位置服务
1. 调用频率限制:
- 基础库 2.17.0 起,
wx.getLocation增加调用频率限制 - 避免高频率调用,推荐使用持续定位接口
2. 高精度定位:
- 开启高精度定位会增加接口耗时
- 可设置
highAccuracyExpireTime控制超时时间
3. 持续定位:
- 使用
wx.onLocationChange进行持续定位 - 页面卸载时调用
wx.stopLocationUpdate停止定位
腾讯位置服务 SDK
1. 申请Key:
- 必须在腾讯位置服务官网申请密钥
- 申请地址:https://lbs.qq.com/dev/console/key/manage
- 体验模式下可免 Key 使用后端服务(通过 apptag 逻辑),详见"前置检查:API Key"章节
2. 商业授权:
- 商业使用需要授权(政府公共事务及公益组织除外)
- 详见腾讯位置服务官网说明
3. 错误处理:
- 注意处理返回的status和message
- 常见错误码:
- 0: 正常
- 310: 请求参数信息有误
- 311: key格式错误
- 110: 请求来源未被授权
性能优化
1. 标记点优化:
- 标记点过多时使用点聚合功能
- 及时移除不需要的标记点
2. 数据更新:
- 避免频繁
setData更新地图数据 - 使用
setting对象批量更新属性
3. 路线优化:
- 路线点位过多时进行抽稀处理
- 使用
polyline的colorList绘制彩虹线
4. 内存管理:
- 及时移除不需要的图层和覆盖物
- 页面卸载时清理地图资源
详细的快速开始指南、常见场景和最佳实践,参见 references/quick_start_and_best_practices.md。
相关链接
{
"name": "tencentmap-miniprogram-skill",
"installedAt": 1776151980531,
"source": "marketplace",
"iconSource": "tencentmap-miniprogram-skill",
"version": "1.0.0"
}App({
onLaunch: function () {
}
})
{
"pages":[
"index/index"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#F8F8F8",
"navigationBarTitleText": "map",
"navigationBarTextStyle":"black"
},
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
}
}
page {
background-color: #F8F8F8;
height: 100%;
font-size: 32rpx;
line-height: 1.6;
}
.page-body {
padding: 20rpx 0;
}
.btn-area{
margin-top: 60rpx;
box-sizing: border-box;
width: 100%;
padding: 0 30rpx;
}
Page({
data: {
latitude: 23.099994,
longitude: 113.324520,
markers: [{
id: 1,
latitude: 23.099994,
longitude: 113.324520,
name: 'T.I.T 创意园'
}],
covers: [{
latitude: 23.099994,
longitude: 113.344520,
iconPath: '/image/location.png'
}, {
latitude: 23.099994,
longitude: 113.304520,
iconPath: '/image/location.png'
}]
},
onReady: function (e) {
this.mapCtx = wx.createMapContext('myMap')
},
getCenterLocation: function () {
this.mapCtx.getCenterLocation({
success: function(res){
console.log(res.longitude)
console.log(res.latitude)
}
})
},
moveToLocation: function () {
this.mapCtx.moveToLocation()
},
translateMarker: function() {
this.mapCtx.translateMarker({
markerId: 1,
autoRotate: true,
duration: 1000,
destination: {
latitude:23.10229,
longitude:113.3345211,
},
animationEnd() {
console.log('animation end')
}
})
},
includePoints: function() {
this.mapCtx.includePoints({
padding: [10],
points: [{
latitude:23.10229,
longitude:113.3345211,
}, {
latitude:23.00229,
longitude:113.3345211,
}]
})
}
})
<view class="page-body">
<view class="page-section page-section-gap">
<map
id="myMap"
style="width: 100%; height: 300px;"
latitude="{{latitude}}"
longitude="{{longitude}}"
markers="{{markers}}"
covers="{{covers}}"
show-location
></map>
</view>
<view class="btn-area">
<button bindtap="getCenterLocation" class="page-body-button" type="primary">获取位置</button>
<button bindtap="moveToLocation" class="page-body-button" type="primary">移动位置</button>
<button bindtap="translateMarker" class="page-body-button" type="primary">移动标注</button>
<button bindtap="includePoints" class="page-body-button" type="primary">缩放视野展示所有经纬度</button>
</view>
</view>
.page-section-gap{
box-sizing: border-box;
padding: 0 30rpx;
}
.page-body-button {
margin-bottom: 30rpx;
}{
"description": "项目配置文件。",
"setting": {
"urlCheck": true,
"es6": true,
"postcss": true,
"minified": true,
"newFeature": true,
"compileWorklet": false,
"uglifyFileName": false,
"uploadWithSourceMap": true,
"enhance": false,
"packNpmManually": false,
"packNpmRelationList": [],
"minifyWXSS": true,
"minifyWXML": true,
"localPlugins": false,
"condition": false,
"swc": false,
"disableSWC": true,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"disableUseStrict": false,
"useCompilerPlugins": false
},
"compileType": "miniprogram",
"libVersion": "1.9.93",
"appid": "wx7fa449c6a5a22a95",
"projectname": "map%E7%BB%84%E4%BB%B6",
"condition": {
"search": {
"current": -1,
"list": []
},
"conversation": {
"current": -1,
"list": []
},
"game": {
"currentL": -1,
"list": []
},
"miniprogram": {
"current": -1,
"list": []
}
},
"simulatorPluginLibVersion": {},
"packOptions": {
"ignore": [],
"include": []
},
"editorSetting": {}
}{
"libVersion": "3.14.2",
"projectname": "minicode-1",
"setting": {
"urlCheck": true,
"coverView": false,
"lazyloadPlaceholderEnable": false,
"skylineRenderEnable": false,
"preloadBackgroundData": false,
"autoAudits": false,
"useApiHook": true,
"showShadowRootInWxmlPanel": false,
"useStaticServer": false,
"useLanDebug": false,
"showES6CompileOption": false,
"compileHotReLoad": true,
"bigPackageSizeSupport": false,
"checkInvalidKey": true,
"ignoreDevUnusedFiles": true
}
}App({
onLaunch: function () {
}
})
{
"pages": [
"index/index"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#F8F8F8",
"navigationBarTitleText": "map",
"navigationBarTextStyle": "black"
},
"sitemapLocation": "sitemap.json"
}page {
background-color: #F8F8F8;
height: 100%;
font-size: 32rpx;
line-height: 1.6;
}
.page-body {
padding: 20rpx 0;
}
.btn-area{
margin-top: 60rpx;
box-sizing: border-box;
width: 100%;
padding: 0 30rpx;
}
const normalCallout = {
id: 1,
latitude: 23.098994,
longitude: 113.322520,
iconPath: '/image/location.png',
callout: {
content: '文本内容',
color: '#ff0000',
fontSize: 14,
borderWidth: 2,
borderRadius: 10,
borderColor: '#000000',
bgColor: '#fff',
padding: 5,
display: 'ALWAYS',
textAlign: 'center'
},
// label: {
// content: 'label 文本',
// fontSize: 24,
// textAlign: 'center',
// borderWidth: 1,
// borderRadius: 5,
// bgColor: '#fff',
// padding: 5
// }
}
const customCallout1 = {
id: 2,
latitude: 23.097994,
longitude: 113.323520,
iconPath: '/image/location.png',
customCallout: {
anchorY: 0,
anchorX: 0,
display: 'ALWAYS'
},
}
const customCallout2 = {
id: 3,
latitude: 23.096994,
longitude: 113.324520,
iconPath: '/image/location.png',
customCallout: {
anchorY: 10,
anchorX: 0,
display: 'ALWAYS',
},
}
const customCallout3 = {
id: 4,
latitude: 23.095994,
longitude: 113.325520,
iconPath: '/image/location.png',
customCallout: {
anchorY: 0,
anchorX: 20,
display: 'ALWAYS',
},
}
const allMarkers = [normalCallout, customCallout1, customCallout2, customCallout3]
Page({
data: {
latitude: 23.096994,
longitude: 113.324520,
markers: [],
customCalloutMarkerIds: [],
num: 1
},
onReady: function (e) {
this.mapCtx = wx.createMapContext('myMap')
},
addMarker() {
const markers = allMarkers
this.setData({
markers,
customCalloutMarkerIds: [2,3,4],
})
},
removeMarker() {
this.setData({
markers: [],
customCalloutMarkerIds: []
})
},
changeMarkerId() {
const customCalloutMarkerIds = this.data.customCalloutMarkerIds.slice()
const top = customCalloutMarkerIds.shift()
customCalloutMarkerIds.push(top)
this.setData({
customCalloutMarkerIds
})
},
markertap(e) {
console.log('@@@ markertap', e)
},
callouttap(e) {
console.log('@@@ callouttap', e)
},
labeltap(e) {
console.log('@@@ labeltap', e)
},
translateMarker: function () {
const length = this.data.markers.length
if (length === 0) return
const index = Math.floor(Math.random() * length)
const markers = this.data.markers
const marker = markers[index]
marker.latitude = marker.latitude + 0.002
marker.longitude = marker.longitude + 0.002
const that = this
this.mapCtx.translateMarker({
markerId: marker.id,
duration: 1000,
destination: {
latitude: marker.latitude,
longitude: marker.longitude
},
animationEnd() {
that.setData({markers})
console.log('animation end')
},
complete(res) {
console.log('translateMarker', res)
}
})
},
changeContent() {
const num = Math.floor(Math.random() * 10)
this.setData({num})
}
})
<view class="page-body">
<view class="page-section page-section-gap">
<map
id="myMap"
style="width: 100%; height: 300px;"
latitude="{{latitude}}"
longitude="{{longitude}}"
bindmarkertap="markertap"
bindcallouttap="callouttap"
bindlabeltap="labeltap"
markers="{{markers}}"
scale="16"
>
<cover-view slot="callout">
<block wx:for="{{customCalloutMarkerIds}}" wx:key="*this">
<cover-view class="customCallout" marker-id="{{item}}" >
<cover-image class="icon" src="/image/voice.png"></cover-image>
<cover-view class="content">
{{num}}-{{item}}-{{index}}
</cover-view>
</cover-view>
</block>
</cover-view>
</map>
</view>
<view class="btn-area">
<button bindtap="addMarker" class="page-body-button" type="primary">添加marker</button>
<button bindtap="removeMarker" class="page-body-button" type="primary">移除所有marker</button>
<button bindtap="translateMarker" class="page-body-button" type="primary">随机移动一个marker</button>
<button bindtap="changeMarkerId" class="page-body-button" type="primary">轮换自定义callout的位置</button>
<button bindtap="changeContent" class="page-body-button" type="primary">改变自定义callout内容</button>
</view>
</view>
.page-section-gap{
box-sizing: border-box;
padding: 0 30rpx;
}
.page-body-button {
margin-bottom: 30rpx;
}
.customCallout {
box-sizing: border-box;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 30px;
width: 150px;
height: 40px;
display: inline-flex;
padding: 5px 20px;
justify-content: center;
align-items: center;
}
.circle {
width: 20px;
height: 20px;
border: 1px solid #ccc;
border-radius: 50%;
}
.icon {
width: 16px;
height: 16px;
}
.content {
flex: 0 1 auto;
margin: 0 10px;
font-size: 14px;
}{
"description": "项目配置文件。",
"setting": {
"urlCheck": false,
"es6": true,
"enhance": false,
"postcss": true,
"preloadBackgroundData": false,
"minified": true,
"newFeature": true,
"coverView": true,
"nodeModules": false,
"autoAudits": false,
"showShadowRootInWxmlPanel": true,
"scopeDataCheck": false,
"uglifyFileName": false,
"checkInvalidKey": true,
"checkSiteMap": true,
"uploadWithSourceMap": true,
"compileHotReLoad": false,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"useIsolateContext": true,
"useCompilerModule": true,
"userConfirmedUseCompilerModuleSwitch": false,
"packNpmManually": false,
"packNpmRelationList": [],
"compileWorklet": false,
"minifyWXSS": true,
"minifyWXML": true,
"localPlugins": false,
"condition": false,
"swc": false,
"disableSWC": true,
"disableUseStrict": false,
"useCompilerPlugins": false
},
"compileType": "miniprogram",
"libVersion": "2.10.2",
"appid": "wx7fa449c6a5a22a95",
"projectname": "cover-view-callout",
"simulatorType": "wechat",
"simulatorPluginLibVersion": {},
"condition": {
"search": {
"current": -1,
"list": []
},
"conversation": {
"current": -1,
"list": []
},
"game": {
"currentL": -1,
"list": []
},
"miniprogram": {
"current": -1,
"list": []
}
},
"packOptions": {
"ignore": [],
"include": []
},
"editorSetting": {}
}{
"libVersion": "3.14.2",
"projectname": "minicode-2",
"setting": {
"urlCheck": false,
"coverView": true,
"lazyloadPlaceholderEnable": false,
"skylineRenderEnable": false,
"preloadBackgroundData": false,
"autoAudits": false,
"useApiHook": true,
"showShadowRootInWxmlPanel": true,
"useStaticServer": false,
"useLanDebug": false,
"showES6CompileOption": false,
"compileHotReLoad": true,
"bigPackageSizeSupport": false,
"checkInvalidKey": true,
"useIsolateContext": true,
"ignoreDevUnusedFiles": true
}
}{
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
"rules": [{
"action": "allow",
"page": "*"
}]
}App({
onLaunch: function () {
}
})
{
"pages":[
"index/index"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Weixin",
"navigationBarTextStyle":"black"
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
const app = getApp()
const img = '../image/location.png'
Page({
data: {
latitude: 23.099994,
longitude: 113.324520,
},
onLoad: function () {
this.mapCtx = wx.createMapContext('mapId')
this.mapCtx.on('markerClusterClick', res =>{
console.log('markerClusterClick', res)
})
// 使用默认聚合效果时可注释下一句
this.bindEvent()
},
bindEvent() {
this.mapCtx.initMarkerCluster({
enableDefaultStyle: false,
zoomOnClick: true,
gridSize: 60,
complete(res) {
console.log('initMarkerCluster', res)
}
})
// enableDefaultStyle 为 true 时不会触发改事件
this.mapCtx.on('markerClusterCreate', res => {
console.log('clusterCreate', res)
const clusters = res.clusters
const markers = clusters.map(cluster => {
const {
center,
clusterId,
markerIds
} = cluster
return {
...center,
width: 0,
height: 0,
clusterId, // 必须
label: {
content: markerIds.length + '',
fontSize: 20,
width: 60,
height: 60,
bgColor: '#00ff00',
borderRadius: 30,
textAlign: 'center',
anchorX: 0,
anchorY: -30,
}
}
})
this.mapCtx.addMarkers({
markers,
clear: false,
complete(res) {
console.log('clusterCreate addMarkers', res)
}
})
})
},
addMarkers() {
const marker = {
id: 1,
iconPath: img,
width: 50,
height: 50,
joinCluster: true, // 指定了该参数才会参与聚合
label:{
width: 50,
height: 30,
borderWidth: 1,
borderRadius: 10,
bgColor: '#ffffff'
}
}
const positions = [
{
latitude: 23.099994,
longitude: 113.324520,
}, {
latitude: 23.099994,
longitude: 113.322520,
}, {
latitude: 23.099994,
longitude: 113.326520,
}, {
latitude: 23.096994,
longitude: 113.329520,
}
]
const markers = []
positions.forEach((p, i) => {
const newMarker = Object.assign(marker, p)
newMarker.id = i + 1
newMarker.label.content = `label ${i + 1}`
markers.push(newMarker)
this.mapCtx.addMarkers({
markers,
clear: false,
complete(res) {
console.log('addMarkers', res)
}
})
})
},
removeMarkers() {
this.mapCtx.addMarkers({
clear: true,
markers: []
})
},
onMarkerTap(e) {
console.log('@@ markertap', e)
},
onCalloutTap(e) {
console.log('@@ onCalloutTap', e)
},
onLabelTap(e) {
console.log('@@ labletap', e)
}
})
{
"usingComponents": {}
}<map
id="mapId"
class="map"
latitude="{{latitude}}"
longitude="{{longitude}}"
bindmarkertap="onMarkerTap"
bindcallouttap="onCalloutTap"
bindlabeltap="onLabelTap"
></map>
<view class="btn-area service">
<button bindtap="removeMarkers">
移除参与聚合点的marker
</button>
<button bindtap="addMarkers">
添加聚合点marker
</button>
</view>.map {
width: 100%;
height: 300px;
}
.btn-area {
margin: 10px;
display: flex;
flex-wrap: wrap;
border: 1px solid #ccc;
}
button {
display: inline-block;
margin: 10px 5px;
}
{
"description": "项目配置文件",
"packOptions": {
"ignore": [],
"include": []
},
"setting": {
"urlCheck": true,
"es6": true,
"enhance": false,
"postcss": true,
"preloadBackgroundData": false,
"minified": true,
"newFeature": false,
"coverView": true,
"nodeModules": false,
"autoAudits": false,
"showShadowRootInWxmlPanel": true,
"scopeDataCheck": false,
"uglifyFileName": false,
"checkInvalidKey": true,
"checkSiteMap": true,
"uploadWithSourceMap": true,
"compileHotReLoad": false,
"useMultiFrameRuntime": false,
"useApiHook": true,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"useIsolateContext": true,
"useCompilerModule": true,
"userConfirmedUseCompilerModuleSwitch": false,
"packNpmManually": false,
"packNpmRelationList": [],
"compileWorklet": false,
"minifyWXSS": true,
"minifyWXML": true,
"localPlugins": false,
"condition": false,
"swc": false,
"disableSWC": true,
"disableUseStrict": false,
"useCompilerPlugins": false
},
"compileType": "miniprogram",
"libVersion": "2.13.2",
"appid": "wx7fa449c6a5a22a95",
"projectname": "%E7%82%B9%E8%81%9A%E5%90%88",
"simulatorType": "wechat",
"simulatorPluginLibVersion": {},
"condition": {
"search": {
"current": -1,
"list": []
},
"conversation": {
"current": -1,
"list": []
},
"game": {
"current": -1,
"list": []
},
"plugin": {
"current": -1,
"list": []
},
"gamePlugin": {
"current": -1,
"list": []
},
"miniprogram": {
"current": -1,
"list": []
}
},
"editorSetting": {}
}{
"libVersion": "3.14.2",
"projectname": "minicode-3",
"setting": {
"urlCheck": true,
"coverView": true,
"lazyloadPlaceholderEnable": false,
"skylineRenderEnable": false,
"preloadBackgroundData": false,
"autoAudits": false,
"useApiHook": true,
"showShadowRootInWxmlPanel": true,
"useStaticServer": false,
"useLanDebug": false,
"showES6CompileOption": false,
"compileHotReLoad": true,
"bigPackageSizeSupport": false,
"checkInvalidKey": true,
"useIsolateContext": true,
"ignoreDevUnusedFiles": true
}
}{
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
"rules": [{
"action": "allow",
"page": "*"
}]
}/**
* 微信小程序JavaScriptSDK
*
* @version 1.2
* @date 2019-03-06
*/
var ERROR_CONF = {
KEY_ERR: 311,
KEY_ERR_MSG: 'key格式错误',
PARAM_ERR: 310,
PARAM_ERR_MSG: '请求参数信息有误',
SYSTEM_ERR: 600,
SYSTEM_ERR_MSG: '系统错误',
WX_ERR_CODE: 1000,
WX_OK_CODE: 200
};
var BASE_URL = 'https://apis.map.qq.com/ws/';
var URL_SEARCH = BASE_URL + 'place/v1/search';
var URL_SUGGESTION = BASE_URL + 'place/v1/suggestion';
var URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/';
var URL_CITY_LIST = BASE_URL + 'district/v1/list';
var URL_AREA_LIST = BASE_URL + 'district/v1/getchildren';
var URL_DISTANCE = BASE_URL + 'distance/v1/';
var URL_DIRECTION = BASE_URL + 'direction/v1/';
var MODE = {
driving: 'driving',
transit: 'transit'
};
var EARTH_RADIUS = 6378136.49;
var Utils = {
/**
* md5加密方法
* 版权所有©2011 Sebastian Tschan,https://blueimp.net
*/
safeAdd(x, y) {
var lsw = (x & 0xffff) + (y & 0xffff);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xffff);
},
bitRotateLeft(num, cnt) {
return (num << cnt) | (num >>> (32 - cnt));
},
md5cmn(q, a, b, x, s, t) {
return this.safeAdd(this.bitRotateLeft(this.safeAdd(this.safeAdd(a, q), this.safeAdd(x, t)), s), b);
},
md5ff(a, b, c, d, x, s, t) {
return this.md5cmn((b & c) | (~b & d), a, b, x, s, t);
},
md5gg(a, b, c, d, x, s, t) {
return this.md5cmn((b & d) | (c & ~d), a, b, x, s, t);
},
md5hh(a, b, c, d, x, s, t) {
return this.md5cmn(b ^ c ^ d, a, b, x, s, t);
},
md5ii(a, b, c, d, x, s, t) {
return this.md5cmn(c ^ (b | ~d), a, b, x, s, t);
},
binlMD5(x, len) {
/* append padding */
x[len >> 5] |= 0x80 << (len % 32);
x[((len + 64) >>> 9 << 4) + 14] = len;
var i;
var olda;
var oldb;
var oldc;
var oldd;
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
for (i = 0; i < x.length; i += 16) {
olda = a;
oldb = b;
oldc = c;
oldd = d;
a = this.md5ff(a, b, c, d, x[i], 7, -680876936);
d = this.md5ff(d, a, b, c, x[i + 1], 12, -389564586);
c = this.md5ff(c, d, a, b, x[i + 2], 17, 606105819);
b = this.md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
a = this.md5ff(a, b, c, d, x[i + 4], 7, -176418897);
d = this.md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
c = this.md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
b = this.md5ff(b, c, d, a, x[i + 7], 22, -45705983);
a = this.md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
d = this.md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
c = this.md5ff(c, d, a, b, x[i + 10], 17, -42063);
b = this.md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
a = this.md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
d = this.md5ff(d, a, b, c, x[i + 13], 12, -40341101);
c = this.md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
b = this.md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
a = this.md5gg(a, b, c, d, x[i + 1], 5, -165796510);
d = this.md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
c = this.md5gg(c, d, a, b, x[i + 11], 14, 643717713);
b = this.md5gg(b, c, d, a, x[i], 20, -373897302);
a = this.md5gg(a, b, c, d, x[i + 5], 5, -701558691);
d = this.md5gg(d, a, b, c, x[i + 10], 9, 38016083);
c = this.md5gg(c, d, a, b, x[i + 15], 14, -660478335);
b = this.md5gg(b, c, d, a, x[i + 4], 20, -405537848);
a = this.md5gg(a, b, c, d, x[i + 9], 5, 568446438);
d = this.md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
c = this.md5gg(c, d, a, b, x[i + 3], 14, -187363961);
b = this.md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
a = this.md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
d = this.md5gg(d, a, b, c, x[i + 2], 9, -51403784);
c = this.md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
b = this.md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
a = this.md5hh(a, b, c, d, x[i + 5], 4, -378558);
d = this.md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
c = this.md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
b = this.md5hh(b, c, d, a, x[i + 14], 23, -35309556);
a = this.md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
d = this.md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
c = this.md5hh(c, d, a, b, x[i + 7], 16, -155497632);
b = this.md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
a = this.md5hh(a, b, c, d, x[i + 13], 4, 681279174);
d = this.md5hh(d, a, b, c, x[i], 11, -358537222);
c = this.md5hh(c, d, a, b, x[i + 3], 16, -722521979);
b = this.md5hh(b, c, d, a, x[i + 6], 23, 76029189);
a = this.md5hh(a, b, c, d, x[i + 9], 4, -640364487);
d = this.md5hh(d, a, b, c, x[i + 12], 11, -421815835);
c = this.md5hh(c, d, a, b, x[i + 15], 16, 530742520);
b = this.md5hh(b, c, d, a, x[i + 2], 23, -995338651);
a = this.md5ii(a, b, c, d, x[i], 6, -198630844);
d = this.md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
c = this.md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
b = this.md5ii(b, c, d, a, x[i + 5], 21, -57434055);
a = this.md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
d = this.md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
c = this.md5ii(c, d, a, b, x[i + 10], 15, -1051523);
b = this.md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
a = this.md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
d = this.md5ii(d, a, b, c, x[i + 15], 10, -30611744);
c = this.md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
b = this.md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
a = this.md5ii(a, b, c, d, x[i + 4], 6, -145523070);
d = this.md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
c = this.md5ii(c, d, a, b, x[i + 2], 15, 718787259);
b = this.md5ii(b, c, d, a, x[i + 9], 21, -343485551);
a = this.safeAdd(a, olda);
b = this.safeAdd(b, oldb);
c = this.safeAdd(c, oldc);
d = this.safeAdd(d, oldd);
}
return [a, b, c, d];
},
binl2rstr(input) {
var i;
var output = '';
var length32 = input.length * 32;
for (i = 0; i < length32; i += 8) {
output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff);
}
return output;
},
rstr2binl(input) {
var i;
var output = [];
output[(input.length >> 2) - 1] = undefined;
for (i = 0; i < output.length; i += 1) {
output[i] = 0;
}
var length8 = input.length * 8;
for (i = 0; i < length8; i += 8) {
output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32);
}
return output;
},
rstrMD5(s) {
return this.binl2rstr(this.binlMD5(this.rstr2binl(s), s.length * 8));
},
rstrHMACMD5(key, data) {
var i;
var bkey = this.rstr2binl(key);
var ipad = [];
var opad = [];
var hash;
ipad[15] = opad[15] = undefined;
if (bkey.length > 16) {
bkey = this.binlMD5(bkey, key.length * 8);
}
for (i = 0; i < 16; i += 1) {
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5c5c5c5c;
}
hash = this.binlMD5(ipad.concat(this.rstr2binl(data)), 512 + data.length * 8);
return this.binl2rstr(this.binlMD5(opad.concat(hash), 512 + 128));
},
rstr2hex(input) {
var hexTab = '0123456789abcdef';
var output = '';
var x;
var i;
for (i = 0; i < input.length; i += 1) {
x = input.charCodeAt(i);
output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f);
}
return output;
},
str2rstrUTF8(input) {
return unescape(encodeURIComponent(input));
},
rawMD5(s) {
return this.rstrMD5(this.str2rstrUTF8(s));
},
hexMD5(s) {
return this.rstr2hex(this.rawMD5(s));
},
rawHMACMD5(k, d) {
return this.rstrHMACMD5(this.str2rstrUTF8(k), str2rstrUTF8(d));
},
hexHMACMD5(k, d) {
return this.rstr2hex(this.rawHMACMD5(k, d));
},
md5(string, key, raw) {
if (!key) {
if (!raw) {
return this.hexMD5(string);
}
return this.rawMD5(string);
}
if (!raw) {
return this.hexHMACMD5(key, string);
}
return this.rawHMACMD5(key, string);
},
/**
* 得到md5加密后的sig参数
* @param {Object} requestParam 接口参数
* @param {String} sk签名字符串
* @param {String} featrue 方法名
* @return 返回加密后的sig参数
*/
getSig(requestParam, sk, feature, mode) {
var sig = null;
var requestArr = [];
Object.keys(requestParam).sort().forEach(function(key){
requestArr.push(key + '=' + requestParam[key]);
});
if (feature == 'search') {
sig = '/ws/place/v1/search?' + requestArr.join('&') + sk;
}
if (feature == 'suggest') {
sig = '/ws/place/v1/suggestion?' + requestArr.join('&') + sk;
}
if (feature == 'reverseGeocoder') {
sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk;
}
if (feature == 'geocoder') {
sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk;
}
if (feature == 'getCityList') {
sig = '/ws/district/v1/list?' + requestArr.join('&') + sk;
}
if (feature == 'getDistrictByCityId') {
sig = '/ws/district/v1/getchildren?' + requestArr.join('&') + sk;
}
if (feature == 'calculateDistance') {
sig = '/ws/distance/v1/?' + requestArr.join('&') + sk;
}
if (feature == 'direction') {
sig = '/ws/direction/v1/' + mode + '?' + requestArr.join('&') + sk;
}
sig = this.md5(sig);
return sig;
},
/**
* 得到终点query字符串
* @param {Array|String} 检索数据
*/
location2query(data) {
if (typeof data == 'string') {
return data;
}
var query = '';
for (var i = 0; i < data.length; i++) {
var d = data[i];
if (!!query) {
query += ';';
}
if (d.location) {
query = query + d.location.lat + ',' + d.location.lng;
}
if (d.latitude && d.longitude) {
query = query + d.latitude + ',' + d.longitude;
}
}
return query;
},
/**
* 计算角度
*/
rad(d) {
return d * Math.PI / 180.0;
},
/**
* 处理终点location数组
* @return 返回终点数组
*/
getEndLocation(location){
var to = location.split(';');
var endLocation = [];
for (var i = 0; i < to.length; i++) {
endLocation.push({
lat: parseFloat(to[i].split(',')[0]),
lng: parseFloat(to[i].split(',')[1])
})
}
return endLocation;
},
/**
* 计算两点间直线距离
* @param a 表示纬度差
* @param b 表示经度差
* @return 返回的是距离,单位m
*/
getDistance(latFrom, lngFrom, latTo, lngTo) {
var radLatFrom = this.rad(latFrom);
var radLatTo = this.rad(latTo);
var a = radLatFrom - radLatTo;
var b = this.rad(lngFrom) - this.rad(lngTo);
var distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLatFrom) * Math.cos(radLatTo) * Math.pow(Math.sin(b / 2), 2)));
distance = distance * EARTH_RADIUS;
distance = Math.round(distance * 10000) / 10000;
return parseFloat(distance.toFixed(0));
},
/**
* 使用微信接口进行定位
*/
getWXLocation(success, fail, complete) {
wx.getLocation({
type: 'gcj02',
success: success,
fail: fail,
complete: complete
});
},
/**
* 获取location参数
*/
getLocationParam(location) {
if (typeof location == 'string') {
var locationArr = location.split(',');
if (locationArr.length === 2) {
location = {
latitude: location.split(',')[0],
longitude: location.split(',')[1]
};
} else {
location = {};
}
}
return location;
},
/**
* 回调函数默认处理
*/
polyfillParam(param) {
param.success = param.success || function () { };
param.fail = param.fail || function () { };
param.complete = param.complete || function () { };
},
/**
* 验证param对应的key值是否为空
*
* @param {Object} param 接口参数
* @param {String} key 对应参数的key
*/
checkParamKeyEmpty(param, key) {
if (!param[key]) {
var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + key +'参数格式有误');
param.fail(errconf);
param.complete(errconf);
return true;
}
return false;
},
/**
* 验证参数中是否存在检索词keyword
*
* @param {Object} param 接口参数
*/
checkKeyword(param){
return !this.checkParamKeyEmpty(param, 'keyword');
},
/**
* 验证location值
*
* @param {Object} param 接口参数
*/
checkLocation(param) {
var location = this.getLocationParam(param.location);
if (!location || !location.latitude || !location.longitude) {
var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误');
param.fail(errconf);
param.complete(errconf);
return false;
}
return true;
},
/**
* 构造错误数据结构
* @param {Number} errCode 错误码
* @param {Number} errMsg 错误描述
*/
buildErrorConfig(errCode, errMsg) {
return {
status: errCode,
message: errMsg
};
},
/**
*
* 数据处理函数
* 根据传入参数不同处理不同数据
* @param {String} feature 功能名称
* search 地点搜索
* suggest关键词提示
* reverseGeocoder逆地址解析
* geocoder地址解析
* getCityList获取城市列表:父集
* getDistrictByCityId获取区县列表:子集
* calculateDistance距离计算
* @param {Object} param 接口参数
* @param {Object} data 数据
*/
handleData(param,data,feature){
if (feature == 'search') {
var searchResult = data.data;
var searchSimplify = [];
for (var i = 0; i < searchResult.length; i++) {
searchSimplify.push({
id: searchResult[i].id || null,
title: searchResult[i].title || null,
latitude: searchResult[i].location && searchResult[i].location.lat || null,
longitude: searchResult[i].location && searchResult[i].location.lng || null,
address: searchResult[i].address || null,
category: searchResult[i].category || null,
tel: searchResult[i].tel || null,
adcode: searchResult[i].ad_info && searchResult[i].ad_info.adcode || null,
city: searchResult[i].ad_info && searchResult[i].ad_info.city || null,
district: searchResult[i].ad_info && searchResult[i].ad_info.district || null,
province: searchResult[i].ad_info && searchResult[i].ad_info.province || null
})
}
param.success(data, {
searchResult: searchResult,
searchSimplify: searchSimplify
})
} else if (feature == 'suggest') {
var suggestResult = data.data;
var suggestSimplify = [];
for (var i = 0; i < suggestResult.length; i++) {
suggestSimplify.push({
adcode: suggestResult[i].adcode || null,
address: suggestResult[i].address || null,
category: suggestResult[i].category || null,
city: suggestResult[i].city || null,
district: suggestResult[i].district || null,
id: suggestResult[i].id || null,
latitude: suggestResult[i].location && suggestResult[i].location.lat || null,
longitude: suggestResult[i].location && suggestResult[i].location.lng || null,
province: suggestResult[i].province || null,
title: suggestResult[i].title || null,
type: suggestResult[i].type || null
})
}
param.success(data, {
suggestResult: suggestResult,
suggestSimplify: suggestSimplify
})
} else if (feature == 'reverseGeocoder') {
var reverseGeocoderResult = data.result;
var reverseGeocoderSimplify = {
address: reverseGeocoderResult.address || null,
latitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lat || null,
longitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lng || null,
adcode: reverseGeocoderResult.ad_info && reverseGeocoderResult.ad_info.adcode || null,
city: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.city || null,
district: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.district || null,
nation: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.nation || null,
province: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.province || null,
street: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street || null,
street_number: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street_number || null,
recommend: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.recommend || null,
rough: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.rough || null
};
if (reverseGeocoderResult.pois) {//判断是否返回周边poi
var pois = reverseGeocoderResult.pois;
var poisSimplify = [];
for (var i = 0;i < pois.length;i++) {
poisSimplify.push({
id: pois[i].id || null,
title: pois[i].title || null,
latitude: pois[i].location && pois[i].location.lat || null,
longitude: pois[i].location && pois[i].location.lng || null,
address: pois[i].address || null,
category: pois[i].category || null,
adcode: pois[i].ad_info && pois[i].ad_info.adcode || null,
city: pois[i].ad_info && pois[i].ad_info.city || null,
district: pois[i].ad_info && pois[i].ad_info.district || null,
province: pois[i].ad_info && pois[i].ad_info.province || null
})
}
param.success(data,{
reverseGeocoderResult: reverseGeocoderResult,
reverseGeocoderSimplify: reverseGeocoderSimplify,
pois: pois,
poisSimplify: poisSimplify
})
} else {
param.success(data, {
reverseGeocoderResult: reverseGeocoderResult,
reverseGeocoderSimplify: reverseGeocoderSimplify
})
}
} else if (feature == 'geocoder') {
var geocoderResult = data.result;
var geocoderSimplify = {
title: geocoderResult.title || null,
latitude: geocoderResult.location && geocoderResult.location.lat || null,
longitude: geocoderResult.location && geocoderResult.location.lng || null,
adcode: geocoderResult.ad_info && geocoderResult.ad_info.adcode || null,
province: geocoderResult.address_components && geocoderResult.address_components.province || null,
city: geocoderResult.address_components && geocoderResult.address_components.city || null,
district: geocoderResult.address_components && geocoderResult.address_components.district || null,
street: geocoderResult.address_components && geocoderResult.address_components.street || null,
street_number: geocoderResult.address_components && geocoderResult.address_components.street_number || null,
level: geocoderResult.level || null
};
param.success(data,{
geocoderResult: geocoderResult,
geocoderSimplify: geocoderSimplify
});
} else if (feature == 'getCityList') {
var provinceResult = data.result[0];
var cityResult = data.result[1];
var districtResult = data.result[2];
param.success(data,{
provinceResult: provinceResult,
cityResult: cityResult,
districtResult: districtResult
});
} else if (feature == 'getDistrictByCityId') {
var districtByCity = data.result[0];
param.success(data, districtByCity);
} else if (feature == 'calculateDistance') {
var calculateDistanceResult = data.result.elements;
var distance = [];
for (var i = 0; i < calculateDistanceResult.length; i++){
distance.push(calculateDistanceResult[i].distance);
}
param.success(data, {
calculateDistanceResult: calculateDistanceResult,
distance: distance
});
} else if (feature == 'direction') {
var direction = data.result.routes;
param.success(data,direction);
} else {
param.success(data);
}
},
/**
* 构造微信请求参数,公共属性处理
*
* @param {Object} param 接口参数
* @param {Object} param 配置项
* @param {String} feature 方法名
*/
buildWxRequestConfig(param, options, feature) {
var that = this;
options.header = { "content-type": "application/json" };
options.method = 'GET';
options.success = function (res) {
var data = res.data;
if (data.status === 0) {
that.handleData(param, data, feature);
} else {
param.fail(data);
}
};
options.fail = function (res) {
res.statusCode = ERROR_CONF.WX_ERR_CODE;
param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
};
options.complete = function (res) {
var statusCode = +res.statusCode;
switch(statusCode) {
case ERROR_CONF.WX_ERR_CODE: {
param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
break;
}
case ERROR_CONF.WX_OK_CODE: {
var data = res.data;
if (data.status === 0) {
param.complete(data);
} else {
param.complete(that.buildErrorConfig(data.status, data.message));
}
break;
}
default:{
param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG));
}
}
};
return options;
},
/**
* 处理用户参数是否传入坐标进行不同的处理
*/
locationProcess(param, locationsuccess, locationfail, locationcomplete) {
var that = this;
locationfail = locationfail || function (res) {
res.statusCode = ERROR_CONF.WX_ERR_CODE;
param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
};
locationcomplete = locationcomplete || function (res) {
if (res.statusCode == ERROR_CONF.WX_ERR_CODE) {
param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
}
};
if (!param.location) {
that.getWXLocation(locationsuccess, locationfail, locationcomplete);
} else if (that.checkLocation(param)) {
var location = Utils.getLocationParam(param.location);
locationsuccess(location);
}
}
};
class QQMapWX {
/**
* 构造函数
*
* @param {Object} options 接口参数,key 为必选参数
*/
constructor(options) {
if (!options.key) {
throw Error('key值不能为空');
}
this.key = options.key;
};
/**
* POI周边检索
*
* @param {Object} options 接口参数对象
*
* 参数对象结构可以参考
* @see http://lbs.qq.com/webservice_v1/guide-search.html
*/
search(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
if (!Utils.checkKeyword(options)) {
return;
}
var requestParam = {
keyword: options.keyword,
orderby: options.orderby || '_distance',
page_size: options.page_size || 10,
page_index: options.page_index || 1,
output: 'json',
key: that.key
};
if (options.address_format) {
requestParam.address_format = options.address_format;
}
if (options.filter) {
requestParam.filter = options.filter;
}
var distance = options.distance || "1000";
var auto_extend = options.auto_extend || 1;
var region = null;
var rectangle = null;
//判断城市限定参数
if (options.region) {
region = options.region;
}
//矩形限定坐标(暂时只支持字符串格式)
if (options.rectangle) {
rectangle = options.rectangle;
}
var locationsuccess = function (result) {
if (region && !rectangle) {
//城市限定参数拼接
requestParam.boundary = "region(" + region + "," + auto_extend + "," + result.latitude + "," + result.longitude + ")";
if (options.sig) {
requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
}
} else if (rectangle && !region) {
//矩形搜索
requestParam.boundary = "rectangle(" + rectangle + ")";
if (options.sig) {
requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
}
} else {
requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend + ")";
if (options.sig) {
requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
}
}
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_SEARCH,
data: requestParam
}, 'search'));
};
Utils.locationProcess(options, locationsuccess);
};
/**
* sug模糊检索
*
* @param {Object} options 接口参数对象
*
* 参数对象结构可以参考
* http://lbs.qq.com/webservice_v1/guide-suggestion.html
*/
getSuggestion(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
if (!Utils.checkKeyword(options)) {
return;
}
var requestParam = {
keyword: options.keyword,
region: options.region || '全国',
region_fix: options.region_fix || 0,
policy: options.policy || 0,
page_size: options.page_size || 10,//控制显示条数
page_index: options.page_index || 1,//控制页数
get_subpois : options.get_subpois || 0,//返回子地点
output: 'json',
key: that.key
};
//长地址
if (options.address_format) {
requestParam.address_format = options.address_format;
}
//过滤
if (options.filter) {
requestParam.filter = options.filter;
}
//排序
if (options.location) {
var locationsuccess = function (result) {
requestParam.location = result.latitude + ',' + result.longitude;
if (options.sig) {
requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest');
}
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_SUGGESTION,
data: requestParam
}, "suggest"));
};
Utils.locationProcess(options, locationsuccess);
} else {
if (options.sig) {
requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest');
}
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_SUGGESTION,
data: requestParam
}, "suggest"));
}
};
/**
* 逆地址解析
*
* @param {Object} options 接口参数对象
*
* 请求参数结构可以参考
* http://lbs.qq.com/webservice_v1/guide-gcoder.html
*/
reverseGeocoder(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
var requestParam = {
coord_type: options.coord_type || 5,
get_poi: options.get_poi || 0,
output: 'json',
key: that.key
};
if (options.poi_options) {
requestParam.poi_options = options.poi_options
}
var locationsuccess = function (result) {
requestParam.location = result.latitude + ',' + result.longitude;
if (options.sig) {
requestParam.sig = Utils.getSig(requestParam, options.sig, 'reverseGeocoder');
}
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_GET_GEOCODER,
data: requestParam
}, 'reverseGeocoder'));
};
Utils.locationProcess(options, locationsuccess);
};
/**
* 地址解析
*
* @param {Object} options 接口参数对象
*
* 请求参数结构可以参考
* http://lbs.qq.com/webservice_v1/guide-geocoder.html
*/
geocoder(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
if (Utils.checkParamKeyEmpty(options, 'address')) {
return;
}
var requestParam = {
address: options.address,
output: 'json',
key: that.key
};
//城市限定
if (options.region) {
requestParam.region = options.region;
}
if (options.sig) {
requestParam.sig = Utils.getSig(requestParam, options.sig, 'geocoder');
}
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_GET_GEOCODER,
data: requestParam
},'geocoder'));
};
/**
* 获取城市列表
*
* @param {Object} options 接口参数对象
*
* 请求参数结构可以参考
* http://lbs.qq.com/webservice_v1/guide-region.html
*/
getCityList(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
var requestParam = {
output: 'json',
key: that.key
};
if (options.sig) {
requestParam.sig = Utils.getSig(requestParam, options.sig, 'getCityList');
}
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_CITY_LIST,
data: requestParam
},'getCityList'));
};
/**
* 获取对应城市ID的区县列表
*
* @param {Object} options 接口参数对象
*
* 请求参数结构可以参考
* http://lbs.qq.com/webservice_v1/guide-region.html
*/
getDistrictByCityId(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
if (Utils.checkParamKeyEmpty(options, 'id')) {
return;
}
var requestParam = {
id: options.id || '',
output: 'json',
key: that.key
};
if (options.sig) {
requestParam.sig = Utils.getSig(requestParam, options.sig, 'getDistrictByCityId');
}
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_AREA_LIST,
data: requestParam
},'getDistrictByCityId'));
};
/**
* 用于单起点到多终点的路线距离(非直线距离)计算:
* 支持两种距离计算方式:步行和驾车。
* 起点到终点最大限制直线距离10公里。
*
* 新增直线距离计算。
*
* @param {Object} options 接口参数对象
*
* 请求参数结构可以参考
* http://lbs.qq.com/webservice_v1/guide-distance.html
*/
calculateDistance(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
if (Utils.checkParamKeyEmpty(options, 'to')) {
return;
}
var requestParam = {
mode: options.mode || 'walking',
to: Utils.location2query(options.to),
output: 'json',
key: that.key
};
if (options.from) {
options.location = options.from;
}
//计算直线距离
if(requestParam.mode == 'straight'){
var locationsuccess = function (result) {
var locationTo = Utils.getEndLocation(requestParam.to);//处理终点坐标
var data = {
message:"query ok",
result:{
elements:[]
},
status:0
};
for (var i = 0; i < locationTo.length; i++) {
data.result.elements.push({//将坐标存入
distance: Utils.getDistance(result.latitude, result.longitude, locationTo[i].lat, locationTo[i].lng),
duration:0,
from:{
lat: result.latitude,
lng:result.longitude
},
to:{
lat: locationTo[i].lat,
lng: locationTo[i].lng
}
});
}
var calculateResult = data.result.elements;
var distanceResult = [];
for (var i = 0; i < calculateResult.length; i++) {
distanceResult.push(calculateResult[i].distance);
}
return options.success(data,{
calculateResult: calculateResult,
distanceResult: distanceResult
});
};
Utils.locationProcess(options, locationsuccess);
} else {
var locationsuccess = function (result) {
requestParam.from = result.latitude + ',' + result.longitude;
if (options.sig) {
requestParam.sig = Utils.getSig(requestParam, options.sig, 'calculateDistance');
}
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_DISTANCE,
data: requestParam
},'calculateDistance'));
};
Utils.locationProcess(options, locationsuccess);
}
};
/**
* 路线规划:
*
* @param {Object} options 接口参数对象
*
* 请求参数结构可以参考
* https://lbs.qq.com/webservice_v1/guide-road.html
*/
direction(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
if (Utils.checkParamKeyEmpty(options, 'to')) {
return;
}
var requestParam = {
output: 'json',
key: that.key
};
//to格式处理
if (typeof options.to == 'string') {
requestParam.to = options.to;
} else {
requestParam.to = options.to.latitude + ',' + options.to.longitude;
}
//初始化局部请求域名
var SET_URL_DIRECTION = null;
//设置默认mode属性
options.mode = options.mode || MODE.driving;
//设置请求域名
SET_URL_DIRECTION = URL_DIRECTION + options.mode;
if (options.from) {
options.location = options.from;
}
if (options.mode == MODE.driving) {
if (options.from_poi) {
requestParam.from_poi = options.from_poi;
}
if (options.heading) {
requestParam.heading = options.heading;
}
if (options.speed) {
requestParam.speed = options.speed;
}
if (options.accuracy) {
requestParam.accuracy = options.accuracy;
}
if (options.road_type) {
requestParam.road_type = options.road_type;
}
if (options.to_poi) {
requestParam.to_poi = options.to_poi;
}
if (options.from_track) {
requestParam.from_track = options.from_track;
}
if (options.waypoints) {
requestParam.waypoints = options.waypoints;
}
if (options.policy) {
requestParam.policy = options.policy;
}
if (options.plate_number) {
requestParam.plate_number = options.plate_number;
}
}
if (options.mode == MODE.transit) {
if (options.departure_time) {
requestParam.departure_time = options.departure_time;
}
if (options.policy) {
requestParam.policy = options.policy;
}
}
var locationsuccess = function (result) {
requestParam.from = result.latitude + ',' + result.longitude;
if (options.sig) {
requestParam.sig = Utils.getSig(requestParam, options.sig, 'direction',options.mode);
}
wx.request(Utils.buildWxRequestConfig(options, {
url: SET_URL_DIRECTION,
data: requestParam
}, 'direction'));
};
Utils.locationProcess(options, locationsuccess);
}
};
module.exports = QQMapWX;快速开始与最佳实践
本文档包含微信小程序地图开发的快速开始指南、常见场景示例、注意事项和最佳实践。
快速开始
基础地图显示
WXML代码:
<map
id="map"
longitude="{{longitude}}"
latitude="{{latitude}}"
scale="16"
show-location="{{true}}"
bindtap="onMapTap"
bindregionchange="onRegionChange">
</map>JavaScript代码:
Page({
data: {
longitude: 116.397128,
latitude: 39.916527
},
onLoad() {
this.getUserLocation()
},
getUserLocation() {
wx.getLocation({
type: 'gcj02', // 使用国测局坐标系
success: (res) => {
this.setData({
longitude: res.longitude,
latitude: res.latitude
})
},
fail: () => {
wx.showModal({
title: '需要位置权限',
content: '请授权位置信息以使用地图功能',
success: (res) => {
if (res.confirm) {
wx.openSetting()
}
}
})
}
})
}
})添加标记点
Page({
data: {
markers: [{
id: 1,
latitude: 39.916527,
longitude: 116.397128,
title: '天安门',
iconPath: '/images/marker.png',
width: 30,
height: 30,
callout: {
content: '北京市中心',
color: '#ffffff',
fontSize: 14,
borderRadius: 5,
bgColor: '#ff0000',
padding: 10,
display: 'ALWAYS'
}
}]
},
onMarkerTap(e) {
console.log('点击标记点', e.detail.markerId)
}
})WXML:
<map
markers="{{markers}}"
bindmarkertap="onMarkerTap"
longitude="{{longitude}}"
latitude="{{latitude}}">
</map>使用腾讯位置服务SDK
引入SDK:
// 将 assets/libs/qqmap-wx-jssdk.js 复制到项目目录
const QQMapWX = require('../../libs/qqmap-wx-jssdk.js')
Page({
onLoad() {
// 实例化API核心类
this.qqmapsdk = new QQMapWX({
key: 'YOUR_KEY' // 在腾讯位置服务官网申请
})
},
// POI搜索
searchPOI(keyword) {
this.qqmapsdk.search({
keyword: keyword,
success: (res) => {
console.log('搜索结果:', res)
},
fail: (error) => {
console.error('搜索失败:', error)
}
})
},
// 路线规划
planRoute(from, to) {
this.qqmapsdk.direction({
mode: 'driving', // 'driving', 'walking', 'bicycling', 'transit'
from: from,
to: to,
success: (res) => {
console.log('路线规划结果:', res)
}
})
}
})地图控制(MapContext)
Page({
onReady() {
this.mapCtx = wx.createMapContext('map')
},
// 获取地图中心点
getCenter() {
this.mapCtx.getCenterLocation({
success: (res) => {
console.log('中心点:', res.longitude, res.latitude)
}
})
},
// 移动到指定位置
moveTo(latitude, longitude) {
this.mapCtx.moveToLocation({
latitude: latitude,
longitude: longitude
})
},
// 轨迹回放
playback(markerId, path) {
this.mapCtx.moveAlong({
markerId: markerId,
path: path,
duration: 5000
})
},
// 添加标记点
addMarker(marker) {
this.mapCtx.addMarkers({
markers: [marker],
success: () => {
console.log('添加成功')
}
})
}
})常见场景
场景1:实时定位追踪
Page({
data: {
longitude: 116.397128,
latitude: 39.916527,
markers: []
},
onLoad() {
this.startLocationUpdate()
},
startLocationUpdate() {
wx.startLocationUpdate({
success: () => {
wx.onLocationChange((res) => {
this.setData({
longitude: res.longitude,
latitude: res.latitude,
markers: [{
id: 1,
latitude: res.latitude,
longitude: res.longitude,
iconPath: '/images/location.png'
}]
})
})
}
})
},
onUnload() {
wx.stopLocationUpdate()
}
})场景2:周边POI搜索
const QQMapWX = require('../../libs/qqmap-wx-jssdk.js')
Page({
onLoad() {
this.qqmapsdk = new QQMapWX({ key: 'YOUR_KEY' })
},
searchNearby(keyword) {
wx.getLocation({
type: 'gcj02',
success: (res) => {
this.qqmapsdk.search({
keyword: keyword,
location: `${res.latitude},${res.longitude}`,
success: (searchRes) => {
const markers = searchRes.data.map((item, index) => ({
id: index,
latitude: item.location.lat,
longitude: item.location.lng,
title: item.title,
callout: {
content: item.title,
display: 'BYCLICK'
}
}))
this.setData({ markers })
}
})
}
})
}
})场景3:路线规划导航
Page({
planRoute(start, end) {
this.qqmapsdk.direction({
mode: 'driving',
from: `${start.lat},${start.lng}`,
to: `${end.lat},${end.lng}`,
success: (res) => {
const coors = res.result.routes[0].polyline
const points = []
// 解码路线坐标
for (let i = 2; i < coors.length; i++) {
coors[i] = coors[i - 2] + coors[i] / 1000000
}
for (let i = 0; i < coors.length; i += 2) {
points.push({
latitude: coors[i],
longitude: coors[i + 1]
})
}
this.setData({
polyline: [{
points: points,
color: '#0091FF',
width: 6,
arrowLine: true
}]
})
}
})
}
})场景4:点聚合
Page({
onReady() {
this.mapCtx = wx.createMapContext('map')
// 初始化点聚合
this.mapCtx.initMarkerCluster({
enableDefaultStyle: false,
zoomOnClick: true,
gridSize: 60
})
// 添加标记点
this.addClusterMarkers()
// 监听聚合创建事件
this.mapCtx.on('markerClusterCreate', (res) => {
const clusters = res.clusters.map(cluster => ({
id: cluster.clusterId,
latitude: cluster.center.lat,
longitude: cluster.center.lng,
iconPath: '/images/cluster.png',
width: 50,
height: 50,
label: {
content: cluster.markerIds.length.toString(),
fontSize: 14
}
}))
this.mapCtx.addMarkers({
markers: clusters,
clear: false
})
})
}
})注意事项
地图组件
1. 坐标系:
- 小程序地图使用 GCJ-02(国测局坐标,火星坐标)
wx.getLocation({ type: 'gcj02' })直接返回GCJ-02坐标- GPS设备返回的WGS-84坐标需要转换
2. 组件尺寸:
<map>标签建议设置宽高
3. 个性化地图:
- 需要在微信公众平台购买并配置
- 使用
subkey参数传入专属KEY - 使用
layer-style指定样式
4. 权限声明:
- 自2022年7月14日后发布的小程序,使用位置接口需在
app.json中声明
{
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
}
}5. API规范(非常重要):
- 所有API调用必须使用文档中定义的接口、属性、事件
- 所有参数必须严格遵守文档格式要求
- 不确定的参数格式请查阅对应demo
位置服务
1. 调用频率限制:
- 基础库 2.17.0 起,
wx.getLocation增加调用频率限制 - 避免高频率调用,推荐使用持续定位接口
2. 高精度定位:
- 开启高精度定位会增加接口耗时
- 可设置
highAccuracyExpireTime控制超时时间
3. 持续定位:
- 使用
wx.onLocationChange进行持续定位 - 页面卸载时调用
wx.stopLocationUpdate停止定位
腾讯位置服务 SDK
1. 申请Key:
- 必须在腾讯位置服务官网申请密钥
- 申请地址:https://lbs.qq.com/dev/console/key/manage
2. 商业授权:
- 商业使用需要授权(政府公共事务及公益组织除外)
- 详见腾讯位置服务官网说明
3. 错误处理:
- 注意处理返回的status和message
- 常见错误码:
- 0: 正常
- 310: 请求参数信息有误
- 311: key格式错误
- 110: 请求来源未被授权
性能优化
1. 标记点优化:
- 标记点过多时使用点聚合功能
- 及时移除不需要的标记点
2. 数据更新:
- 避免频繁
setData更新地图数据 - 使用
setting对象批量更新属性
3. 路线优化:
- 路线点位过多时进行抽稀处理
- 使用
polyline的colorList绘制彩虹线
4. 内存管理:
- 及时移除不需要的图层和覆盖物
- 页面卸载时清理地图资源
最佳实践
1. 权限处理
// 完整的权限检查和处理流程
checkAndRequestLocation() {
wx.getSetting({
success: (res) => {
if (res.authSetting['scope.userLocation']) {
// 已授权,直接获取位置
this.getLocation()
} else if (res.authSetting['scope.userLocation'] === false) {
// 用户之前拒绝过,引导去设置
wx.showModal({
title: '位置权限',
content: '需要位置权限才能使用此功能,请在设置中开启',
confirmText: '去设置',
success: (modalRes) => {
if (modalRes.confirm) {
wx.openSetting()
}
}
})
} else {
// 未授权过,发起授权请求
wx.authorize({
scope: 'scope.userLocation',
success: () => {
this.getLocation()
},
fail: () => {
wx.showToast({
title: '授权失败',
icon: 'none'
})
}
})
}
}
})
}2. 地图初始化配置
// 使用setting对象统一管理地图配置
Page({
data: {
setting: {
skew: 0,
rotate: 0,
showLocation: true,
showScale: true,
enableZoom: true,
enableScroll: true,
enableRotate: false,
showCompass: false,
enable3D: false,
enableOverlooking: false,
enableSatellite: false,
enableTraffic: false
}
},
updateMapSetting(newSettings) {
this.setData({
setting: {
...this.data.setting,
...newSettings
}
})
}
})3. 错误处理
// 统一的错误处理
handleLocationError(error) {
console.error('位置错误:', error)
// 提供默认位置作为降级方案
this.setData({
longitude: 116.397128,
latitude: 39.916527
})
wx.showToast({
title: '获取位置失败,使用默认位置',
icon: 'none'
})
}4. 数据格式规范
// 标记点数据格式
const markerData = {
id: 1, // 必填:标记点id
latitude: 39.916527, // 必填:纬度
longitude: 116.397128, // 必填:经度
iconPath: '/images/marker.png', // 必填:图标路径
width: 30, // 可选:图标宽度
height: 30, // 可选:图标高度
title: '标题', // 可选:标题
callout: { // 可选:气泡
content: '气泡内容',
color: '#ffffff',
fontSize: 14,
borderRadius: 5,
bgColor: '#ff0000',
padding: 10,
display: 'ALWAYS'
}
}
// 路线数据格式
const polylineData = {
points: [ // 必填:坐标点数组
{ latitude: 39.916527, longitude: 116.397128 },
{ latitude: 39.926527, longitude: 116.407128 }
],
color: '#0091FF', // 可选:线条颜色
width: 6, // 可选:线条宽度
arrowLine: true, // 可选:是否带箭头
dottedLine: false // 可选:是否虚线
}>(产品通知)<br> 由于 /ws/distance/v1 接口已逐步下线,后续不再进行维护,建议您通过WebService API接口的距离矩阵实现相关需求,接入方法请参考微信小程序中使用服务API
calculateDistance(options:Object)
计算一个点到多点的步行、驾车距离。
注:坐标系采用gcj02坐标系
</br>
options属性说明
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mode | String | 否 | 可选值:'driving'(驾车)、'walking'(步行),默认:'walking'</br>新增直线距离计算,'straight'(直线)直线距离计算适用于 jssdkv1.1 jssdkv1.2 |
| from | StringIObject | 是 | 位置坐标,</br>①String格式:lat<纬度>,lng<经度>(例:from: '39.984060,116.307520')</br>②Object格式:</br>{</br> latitude: 纬度,</br> longitude: 经度</br>}</br>(例:from: { </br>latitude: 39.984060,</br>longitude: 116.307520</br>})</br>默认是当前位置 |
| to | StringIObject | 是 | 终点坐标,</br>①String格式:lat,lng;lat,lng... (经度与纬度用英文逗号分隔,坐标间用英文分号分隔)</br>(例:to: '39.984060,116.307520;39.984060,116.507520')</br>②Object格式1:</br>[{</br> latitude: 纬度,</br> longitude: 经度</br>}, ...]</br>(例:to:[{</br> latitude:39.984060,</br> longitude:116.307520</br>},...])</br>③Objec格式2:</br>此格式主要对应search返回的数据结构格式,方便开发这批量转换</br>[{</br>location: {</br> lat: 纬度,</br> lng: 经度</br>}</br>}, ...]</br>(例:to:[{</br>location:{</br> lat:39.984060,</br> lng:116.307520</br>}</br>},...]) |
| sig | String | 否 | 签名校验</br>开启WebServiceAPI签名校验的必传参数,只需要传入生成的SK字符串即可,不需要进行MD5加密操作</br>该参数适用于 jssdkv1.1 jssdkv1.2 |
调用结果
通过属性success, fail, complete的回调参数来接收调用结果
success的回调参数可以有2个,第1个参数接收调用结果,第2个参数控制返回处理后的数据(非必须参数),示例:success:function(res,data)
该属性适用于 jssdkv1.1 jssdkv1.2
回调结果对象
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | number | 是 | 状态码,0为正常,310请求参数信息有误,311Key格式错误,306请求有护持信息请检查字符串,110请求来源未被授权 |
| message | string | 是 | 状态说明,即对状态码status进行说明 |
| result | CalculateDistanceResult | 是 | 计算结果 |
CalculateDistanceResult 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| elements | DistanceElement[] | 是 | 结果数组 |
DistanceElement 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| from | LatLngObject | 是 | 起点坐标 |
| to | LatLngObject | 是 | 终点坐标 |
| distance | number | 是 | 起点到终点的距离,单位:米,如果radius半径过小或者无法搜索到,则返回-1 |
| duration | number | 是 | 表示从起点到终点的结合路况的时间,秒为单位。注:步行方式不计算耗时,该值始终为0 |
LatLngObject 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| lat | number | 是 | 纬度 |
| lng | number | 是 | 经度 |
示例
WXML 模板文件中添加map组件,并绑定markers数据: ``` html <!--form表单--> <form bindsubmit="formSubmit"> <!--输入起点和终点经纬度坐标,格式为string格式--> <label>起点坐标: <input style="border:1px solid #000;" name="start"></input> </label> <!--多个终点位置示例:39.984060,116.307520;39.984060,116.507520--> <label>终点坐标: <input style="border:1px solid #000;" name="dest"></input> </label> <!--提交表单数据--> <button form-type="submit">计算距离</button> </form> <!--渲染起点经纬度到终点经纬度距离,单位为米--> <view wx:for="{{distance}}" wx:key="index"> <view>起点到终点{{index+1}}的步行距离为{{item}}米</view> </view>
Javascript 关键代码片段:// 引入SDK核心类 var QQMapWX = require('xxx/qqmap-wx.js');
// 实例化API核心类 var qqmapsdk = new QQMapWX({ key: '开发密钥(key)' // 必填 });
//在Page({})中使用下列代码 Page({ data: { distance: [], }, //事件触发,调用接口 formSubmit(e){ var _this = this; //调用距离计算接口 qqmapsdk.calculateDistance({ //mode: 'driving',//可选值:'driving'(驾车)、'walking'(步行),不填默认:'walking',可不填 //from参数不填默认当前地址 //获取表单提交的经纬度并设置from和to参数(示例为string格式) from: e.detail.value.start || '', //若起点有数据则采用起点坐标,若为空默认当前地址 to: e.detail.value.dest, //终点坐标 success: function(res) {//成功后的回调 console.log(res); var res = res.result; var dis = []; for (var i = 0; i < res.elements.length; i++) { dis.push(res.elements[i].distance); //将返回数据存入dis数组, } _this.setData({ //设置并更新distance数据 distance: dis }); }, fail: function(error) { console.error(error); }, complete: function(res) { console.log(res); } }); } })
## 接口调用说明
calculateDistance(options:Object)方法调用接口服务如下:
- /ws/distance/v1 距离计算:步行
- /ws/distance/v1 距离计算:驾车
注:微信小程序JavaScript SDK通过对腾讯位置服务WebServiceAPI接口进行封装而形成,因此和直接调用WebSerivceAPI的限制是等同的,<br>具体可参考:[腾讯位置服务WebServiceAPI配额及使用限制](https://lbs.qq.com/dev/console/quotaImprove)
direction(options:Object)
提供路线规划能力。
1.驾车(driving):支持结合实时路况、少收费、不走高速等多种偏好,精准预估到达时间(ETA);
2.步行(walking):基于步行路线规划。
3.骑行(bicycling):基于自行车的骑行路线;
4.公交(transit):支持公共汽车、地铁等多种公共交通工具的换乘方案计算;
注:坐标系采用gcj02坐标系
路线规划功能仅适用于 jssdkv1.2 请前往首页下载最新版本SDK
</br>
options属性说明
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| 驾车,步行,骑行,公交路线规划公共参数: | |||
| mode | String | 否 | 路线规划选择,可选值:'driving'(驾车)、'walking'(步行)、'bicycling'(骑行)、'transit'(公交),默认:'driving' |
| from | String | 是 | 位置坐标,①String格式:String格式:lat<纬度>,lng<经度>(例:from: '39.984060,116.307520')</br>②Object格式:</br>{</br> latitude: 纬度,</br> longitude: 经度</br>}</br>(例:from: {</br> latitude: 39.984060,</br> longitude: 116.307520</br>})</br>默认是当前位置 |
| to | String | 是 | 位置坐标,</br>①String格式:lat<纬度>,lng<经度>(例:location: '39.984060,116.307520')</br>②Object格式:</br>{</br> latitude: 纬度,</br> longitude: 经度</br>}</br>(例:to: {</br> latitude: 39.984060,</br> longitude: 116.307520</br>}) |
| sig | String | 否 | 签名校验</br>开启WebServiceAPI签名校验的必传参数,只需要传入生成的SK字符串即可,不需要进行MD5加密操作 |
| driving其他参数: | |||
| from_poi | Number | 否 | 起点POI ID,传入后,优先级高于from(坐标)</br>示例:from_poi:4077524088693206111 |
| heading | Number | 否 | [from辅助参数]在起点位置时的车头方向,数值型,取值范围0至360(0度代表正北,顺时针一周360度)</br>传入车头方向,对于车辆所在道路的判断非常重要,直接影响路线计算的效果</br>示例:heading:175 |
| speed | Number | 否 | [from辅助参数]速度,单位:米/秒,默认3。 当速度低于1.39米/秒时,heading将被忽略</br>示例:speed:5 |
| accuracy | Number | 否 | [from辅助参数]定位精度,单位:米,取>0数值,默认5。 当定位精度>30米时heading参数将被忽略</br>示例:accuracy:30 |
| road_type | Number | 否 | [from辅助参数] 起点道路类型,可选值:</br>0 [默认]不考虑起点道路类型</br>1 在桥上;2 在桥下;3 在主路;4 在辅路;5 在对面;6 桥下主路;7 桥下辅路 |
| from_track | String | 否 | 起点轨迹:</br>在真实的场景中,易受各种环境及设备精度影响,导致定位点产生误差,传入起点前段轨迹,可有效提升准确度。</br>优先级: 高于from参数</br>轨迹中的每个定位点包含以下信息:</br>1.纬度</br>2.经度</br>3.速度:GPS速度,单位 米/秒,取不到传-1</br>4.精度:GPS精度, 单位毫米,取不到传-1</br>5.运动方向: gps方向,正北为0, 顺时针夹角,[0-360),获取不到时传-1</br>6.设备方向:正北为0, 顺时针夹角,[0-360),取不到传-1</br>7.时间:定位获取该点的时间,Unix时间戳,取不到传0</br>参数格式:</br>1.轨迹中最多支持传入50个点。</br>2.每个点之间英文分号分隔,时间顺序由旧到新(第一个点最早获取,最后一个点最新得到)</br>3.每个点中的信息用英文逗号分隔,并按以下顺序传入:</br>纬度,经度,速度,精度,运动方向,设备方向,时间;第2个点;第3个点……</br>示例:</br>from_track:'40.037029,116.316633,16,500,160,-1,1529491290;40.036634,116.317170,16,500,161,-1,</br>1529491300;40.035977,116.317663,16,500,159,-1,1529491310;40.035706,116.318328,16,500,160,-1,1529491320;</br>40.035090,116.319058,16,500,160,-1,1529491330;40.034852,116.319820,16,500,160,-1,1529491340' |
| to_poi | Number | 否 | 终点POI ID(可通过腾讯位置服务地点搜索服务得到),当目的地为较大园区、小区时,会以引导点做为终点(如出入口等),体验更优。</br>该参数优先级高于to(坐标),但是当目的地无引导点数据或POI ID失效时,仍会使用to(坐标)作为终点</br>示例:to_poi:5371594408805356897 |
| waypoints | String | 否 | 途经点,格式:lat1,lng1;lat2,lng2;… 最大支持30个</br>示例:waypoints:39.951004,116.571980 |
| policy | String | 否 | 一、策略参数(以下三选一)</br>LEAST_TIME:[默认]参考实时路况,时间最短</br>PICKUP:网约车场景 – 接乘客</br>TRIP:网约车场景 – 送乘客</br>二、单项偏好参数</br>(可与策略参数并用,可多选,逗号分隔)</br>REAL_TRAFFIC:参考实时路况</br>LEAST_FEE:少收费</br>AVOID_HIGHWAY:不走高速</br>NAV_POINT_FIRST: 该策略会通过终点坐标查找所在地点(如小区/大厦等),并使用地点出入口做为目的地,使路径更为合理</br>示例:policy:'LEAST_TIME'或者policy:'LEAST_TIME,AVOID_HIGHWAY' |
| plate_number | String | 否 | 车牌号,填入后,路线引擎会根据车牌对限行区域进行避让,不填则不不考虑限行问题</br>示例:plate_number:京X309KX |
| transit其他参数: | |||
| departure_time | Number | 否 | 出发时间,默认使用当前时间,用于过滤掉非运营时段的线路</br>示例:departure_time:1509357129 |
| policy | String | 否 | 1) 排序策略,以下三选一:</br>policy=LEAST_TIME:时间短(默认)</br>policy=LEAST_TRANSFER:少换乘</br>policy=LEAST_WALKING:少步行<br/>policy=RECOMMEND:推荐策略,结合步行、换乘、耗时等多方面综合排序结果(与腾讯地图APP默认策略一致)</br>2) 额外限制条件</br>(可与排序策略配合使用,如:policy=LEAST_TRANSFER,NO_SUBWAY):</br>NO_SUBWAY ,不坐地铁</br>ONLY_SUBWAY:只坐地铁 <br>SUBWAY_FIRST:地铁优先<br>示例:policy:'LEAST_TIME' 或者 policy:'LEAST_TIME,AVOID_HIGHWAY' |
调用结果
通过属性success, fail, complete的回调参数来接收调用结果
success的回调参数可以有2个,第1个参数接收调用结果,第2个参数控制返回处理后的数据(非必须参数),示例:success:function(res,data)
driving返回结果
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | number | 是 | 状态码,正常为0 |
| message | string | 是 | 状态说明 |
| result | DrivingResult | 是 | 搜索结果 |
DrivingResult 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| routes | DrivingRoute[] | 是 | 路线方案数组 |
DrivingRoute 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mode | string | 是 | 方案交通方式,固定值:"DRIVING" |
| tags | string[] | 否 | 方案标签,用于表明方案特点</br>取值:EXPERIENCE(经验路线)、RECOMMEND(推荐路线)、LEAST_LIGHT(红绿灯少)、LEAST_TIME(时间最短)、LEAST_DISTANCE(距离最短) |
| distance | number | 是 | 方案总距离 |
| duration | number | 是 | 方案估算时间(含路况) |
| restriction | Restriction | 否 | 限行信息 |
| polyline | number[] | 是 | 方案路线坐标点串(该点串经过压缩,解压请参考:polyline 坐标解压) |
| waypoints | Waypoint[] | 否 | 途经点,顺序与输入waypoints一致 |
| taxi_fare | TaxiFare | 否 | 预估打车费 |
| steps | DrivingStep[] | 是 | 路线步骤 |
Restriction 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | number | 是 | 限行状态码:0(途经没有限行城市)、1(途经包含有限行的城市)、3(已避让限行)、4(无法避开限行区域) |
Waypoint 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| title | string | 否 | 途经点路名 |
| location | LatLngObject | 否 | 途经点坐标 |
TaxiFare 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| fare | number | 否 | 预估打车费用,单位:元 |
DrivingStep 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| instruction | string | 是 | 阶段路线描述 |
| polyline_idx | number[] | 是 | 阶段路线坐标点串在方案路线坐标点串的位置 |
| road_name | string | 否 | 阶段路线路名 |
| dir_desc | string | 否 | 阶段路线方向 |
| distance | number | 是 | 阶段路线距离 |
| act_desc | string | 否 | 阶段路线末尾动作 |
| accessorial_desc | string | 否 | 末尾辅助动作 |
walking返回结果
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | number | 是 | 状态码,正常为0 |
| message | string | 是 | 状态说明 |
| result | WalkingResult | 是 | 搜索结果 |
WalkingResult 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| routes | WalkingRoute[] | 是 | 路线方案数组 |
WalkingRoute 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mode | string | 是 | 方案交通方式,固定值:"WALKING" |
| distance | number | 是 | 方案整体距离(米) |
| duration | number | 是 | 方案估算时间(分钟) |
| direction | string | 是 | 方案整体方向 |
| polyline | number[] | 是 | 方案路线坐标点串 |
| steps | WalkingStep[] | 是 | 路线步骤 |
WalkingStep 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| instruction | string | 是 | 阶段路线描述 |
| polyline_idx | number[] | 是 | 阶段路线坐标点串在方案路线坐标点串的位置 |
| road_name | string | 否 | 阶段路线路名 |
| act_desc | string | 否 | 阶段路线方向/末尾动作 |
| distance | number | 是 | 阶段路线距离 |
bicycling返回结果
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | number | 是 | 状态码,正常为0 |
| message | string | 是 | 状态说明 |
| result | BicyclingResult | 是 | 搜索结果 |
BicyclingResult 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| routes | BicyclingRoute[] | 是 | 路线方案数组 |
BicyclingRoute 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mode | string | 是 | 方案交通方式,固定值:"BICYCLING" |
| distance | number | 是 | 方案整体距离(米) |
| duration | number | 是 | 方案估算时间(分钟) |
| direction | string | 是 | 方案整体方向 |
| polyline | number[] | 是 | 方案路线坐标点串 |
| steps | BicyclingStep[] | 是 | 路线步骤 |
BicyclingStep 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| instruction | string | 是 | 阶段路线描述 |
| polyline_idx | number[] | 是 | 阶段路线坐标点串在方案路线坐标点串的位置 |
| road_name | string | 否 | 阶段路线路名 |
| act_desc | string | 否 | 阶段路线方向/末尾动作 |
| distance | number | 是 | 阶段路线距离 |
transit返回结果
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | number | 是 | 状态码,正常为0 |
| message | string | 是 | 状态说明 |
| result | TransitResult | 是 | 搜索结果 |
TransitResult 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| routes | TransitRoute[] | 是 | 路线方案数组 |
TransitRoute 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| distance | number | 是 | 方案整体距离(米) |
| duration | number | 是 | 方案估算时间(分钟) |
| bounds | string | 是 | 路线bounds,用于显示地图时使用 |
| steps | TransitStep[] | 是 | 具体方案,按交通工具的换乘分段 |
TransitStep 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mode | string | 是 | 阶段路线交通方式:WALKING(步行)、TRANSIT(公交/地铁)、TRANSIT_FOLDER(跨城公交) |
| tag | string | 否 | 返回值仅支持INTERNAL,代表站内换乘 |
| distance | number | 是 | 阶段路线距离 |
| duration | number | 否 | 阶段路线估算时间 |
| direction | string | 否 | 阶段路线方向 |
| polyline | number[] | 否 | 阶段路线点串 |
| steps | WalkingStep[] | 否 | 步行分段(mode为WALKING时) |
| lines | TransitLine[] | 否 | 交通线路数组(mode为TRANSIT时) |
| origin | TransitLocation | 否 | 起点(mode为TRANSIT_FOLDER时) |
| destination | TransitLocation | 否 | 终点(mode为TRANSIT_FOLDER时) |
TransitLine 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| vehicle | string | 是 | 交通工具:BUS(公交车)、SUBWAY(地铁)、RAIL(铁路) |
| id | string | 是 | 线路ID |
| title | string | 是 | 线路名称 |
| station_count | number | 是 | 途径/经过站数 |
| price | number | 否 | 票价 |
| destination | TransitStation | 否 | 终点站/目的地 |
| start_time | string | 否 | 首班车时间 |
| end_time | string | 否 | 末班车时间 |
| distance | number | 是 | 距离 |
| duration | number | 是 | 估算时间 |
| polyline | number[] | 是 | 阶段路线坐标点串 |
| geton | TransitStop | 是 | 上车站 |
| getoff | TransitStop | 是 | 下车站 |
| stations | TransitStop[] | 是 | 途经站数组 |
TransitStop 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 站点ID |
| title | string | 是 | 站点名称 |
| location | LatLngObject | 是 | 站点坐标 |
| exit | TransitExit | 否 | 地铁出口信息 |
TransitExit 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 出口ID |
| title | string | 是 | 出口名称 |
TransitStation 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 站点ID |
| title | string | 是 | 站点名称 |
TransitLocation 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| location | LatLngObject | 是 | 坐标 |
LatLngObject 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| lat | number | 是 | 纬度 |
| lng | number | 是 | 经度 |
</br>
(a.公交同城路线说明:)
路线根据交通工具的不同划分路段,如下图:

</br>
响应结果示例 ``` javascript { "status":0, //状态码,正常为0 "message":"query ok", //状态说明 "result": //搜索结果 { "routes":[ //路线方案 //方案1******************************************* { "distance": 20123, //方案整体距离(米) "duration": 75, //方案估算时间(分钟) "duration_1m":75, "bounds": "40.014309000000004,116.285706,40.15301,116.318099", //路线bounds,用于显示地图时使用 "steps": [ //具体方案,按交通工具的换乘分段 { "mode": "WALKING", //阶段路线交通方式(WALKING) "tag": "INTERNAL", //返回值仅支持INTERNAL,代表站内换乘 "distance": 760, //阶段路线距离 "duration": 11, //阶段路线估算时间 "direction": "东", //阶段路线方向 "polyline": [40.014634, 116.312869, -135, 160, -90, 100, -40, 80,... ...], //阶段路线点串 "steps": //分段 [{ "instruction": "步行454米,左转进入中关村北大街", //阶段路线描述 "polyline_idx": [0, 31],//在polyline位置 "road_name": "", //路名 "dir_desc": "东南", //方向 "distance": 454, //距离 "act_desc": "左转" //末尾动作 }] }, { "mode": "TRANSIT", "lines": [ //lines指同一段路,三种出行方式 { "vehicle": "BUS", //交通工具:公交车(BUS) "id": "7225403257131337003", "title": "717", //公交名 "station_count": 7, //途径站数 "price": 2, //票价 "destination":{ "id":"5047847604295657152", "title":"地铁西二旗站" //终点站 }, "start_time":"05:30", //首班车时间 "end_time":"22:00", //末班车时间 "distance": 4560, //距离 "duration": 26, //估算时间 "polyline": [40.016124, 116.317759, 423, -241, 321, -214, 410, -323, 378, ... ...], "geton": { //上车站 "id": "10640224892745223512", "title": "圆明园东门", //上车站名 "location": { //上车站坐标 "lat": 40.016124, //纬度 "lng": 116.317759 //经度 } }, "getoff": { //下车站 "id": "5047847604295657152", "title": "地铁西二旗站", //下车站名 "location": { //下车站坐标 "lat": 40.053226, //纬度 "lng": 116.304718 //经度 } }, "stations": [ //途经站 { "id": "17005795237013591570", "title": "北京体育大学", //途经站名 "location": { //途经站坐标 "lat": 40.021902, //纬度 "lng": 116.314189 //经度 } } ] } ] } ] } ] } }
</br>
(**b.跨城说明:**)
路线跨城支持城市间的铁路路线规划,如图(A城火车站)b - c(B城火车站) 路线规划方案; 市内路线(如图a - b或c - d)需要使用mode = TRANSIT_FOLDER节点进行二次查询得到。

</br>
**响应结果示例**{ "status":0, //状态码,正常为0 "message":"query ok", //状态说明 "result": //搜索结果 { "routes":[ //路线方案 { "distance": 1337851, //方案整体距离(米) "duration":288, //方案估算时间(分钟) "bounds": "31.194238,116.378922,39.871814,121.320876", //路线bounds,用于显示地图时使用 "steps": [ //具体方案,按交通工具的换乘分段 { "mode": "TRANSIT_FOLDER", //TRANSIT_FOLDER跨城公交 "distance": 4, //阶段路线距离 "direction": "北", //阶段路线方向 "origin":{ //起点 "location":{ //起点坐标 "lat":"39.864989", //纬度 "lng":"116.379009" //经度 } }, "destination":{ //终点 "location":{ //终点坐标 "lat":"39.865021", //纬度 "lng":"116.379019" //经度 } } }, { "mode": "TRANSIT", "lines": [ { "vehicle": "RAIL", //交通工具:铁路(RAIL) "id": "13995286317411406868", "station_count": 2, //经过站数 "destination":{ //目的地 "id":"13136947784159182883", "title":"上海虹桥" }, "distance": 1318000, //距离 "duration": 288, //估算时间 "polyline": [39.865021, 116.379019, 58, -97, 1676, 3233, 5059, 37384, -1061, ... ...], "geton": { //本段上车站 "id": "17252417566629421518", "title": "北京南", //上车站名 "location": { //上车站坐标 "lat": 39.865021, //纬度 "lng": 116.379019 //经度 } }, "getoff": { //本段下车站 "id": "13136947784159182883", "title": "上海虹桥", //下车站名 "location": { //下车站坐标 "lat": 31.194238, //纬度 "lng": 121.320876 //经度 } }, "stations": [ //途经站 { "id": "17403057985479983304", "title": "德州东站", //途经站名 "location": { //途经站坐标 "lat": 37.409700, //纬度 "lng": 116.462110 //经度 } } ] } ] } ] } ] } }
</br>
## polyline 坐标解压
导航路线点串coors(注:coors为polyline的坐标串)使用前向差分进行压缩,使用时解压方法如下:
> var coors=[127.496637,50.243916,-345,-1828,19867,-26154];</br>
for (var i = 2; i < coors.length ; i++)</br>
{coors[i] = coors[i-2] + coors[i]/1000000}</br>
</br>
## 注意事项
起点、终点、途经点若不在道路上,会自动吸附到附近的道路上:

</br>
**示例(以驾车路线规划为例)**
WXML 模板文件中添加组件:<!--地图容器--> <map id="myMap" style="width: 100%; height: 300px;" longitude="{{longitude}}" latitude="{{latitude}}" scale='16' polyline="{{polyline}}" show-location > </map> <form bindsubmit="formSubmit"> <!--输入起点和目的地经纬度坐标,格式为string格式--> <!--起点输入框,同终点,不填默认当前位置--> <label>起点坐标:<input style="border:1px solid #000;" name="start"></input></label> <!--终点输入框,例:39.984060,116.307520--> <label>终点坐标:<input style="border:1px solid #000;" name="dest"></input></label> <!--提交表单数据--> <button form-type="submit">路线规划</button> </form>
Javascript 关键代码片段(驾车、步行、骑行路线规划):// 引入SDK核心类 var QQMapWX = require('xxx/qqmap-wx.js');
// 实例化API核心类 var qqmapsdk = new QQMapWX({ key: '开发密钥(key)' // 必填 });
//在Page({})中使用下列代码 Page({ data: { polyline:[], // 声明polyline,存储路线数据 // 展示地图当前位置 latitude: 39.908764, longitude: 116.397524 },
//触发表单提交事件,调用接口 formSubmit(e) { var _this = this; //调用距离计算接口 qqmapsdk.direction({ mode: 'driving',//可选值:'driving'(驾车)、'walking'(步行)、'bicycling'(骑行) //from参数不填默认当前地址 from: e.detail.value.start, to: e.detail.value.dest, success: function (res) { console.log(res); var ret = res; var coors = ret.result.routes[0].polyline, pl = []; //坐标解压(返回的点串坐标,通过前向差分进行压缩) var kr = 1000000; for (var i = 2; i < coors.length; i++) { coors[i] = Number(coors[i - 2]) + Number(coors[i]) / kr; } //将解压后的坐标放入点串数组pl中 for (var i = 0; i < coors.length; i += 2) { pl.push({ latitude: coors[i], longitude: coors[i + 1] }) } console.log(pl) //设置polyline属性,将路线显示出来 _this.setData({ latitude:pl[0].latitude, longitude:pl[0].longitude, polyline: [{ points: pl, color: '#FF0000DD', width: 4 }] }) }, fail: function (error) { console.error(error); }, complete: function (res) { console.log(res); } }); } })
Javascript 关键代码片段(公交路线规划,取第一条路线为示例):// 引入SDK核心类 var QQMapWX = require('xxx/qqmap-wx.js');
// 实例化API核心类 var qqmapsdk = new QQMapWX({ key: '开发密钥(key)' // 必填 });
//在Page({})中使用下列代码 Page({ data: { polyline:[], // 声明polyline,存储路线数据 latitude: 39.908764, longitude: 116.397524 }, //触发表单提交事件,调用接口 formSubmit(e) { var _this = this; qqmapsdk.direction({ mode: 'transit',//'transit'(公交路线规划) from: e.detail.value.start, to: e.detail.value.dest, success: function (res) { console.log(res); var ret = res.result.routes[0]; var count = ret.steps.length; var pl = []; var coors = []; //获取各个步骤的polyline for(var i = 0; i < count; i++) { if (ret.steps[i].mode == 'WALKING' && ret.steps[i].polyline) { coors.push(ret.steps[i].polyline); } if (ret.steps[i].mode == 'TRANSIT' && ret.steps[i].lines[0].polyline) { coors.push(ret.steps[i].lines[0].polyline); } } //坐标解压 var kr = 1000000; for (var i = 0 ; i < coors.length; i++){ for (var j = 2; j < coors[i].length; j++) { coors[i][j] = Number(coors[i][j - 2]) + Number(coors[i][j]) / kr; } } //定义新数组,将coors中的数组合并为一个数组 var coorsArr = []; for (var i = 0 ; i < coors.length; i ++){ coorsArr = coorsArr.concat(coors[i]); } //将解压后的坐标放入点串数组pl中 for (var i = 0; i < coorsArr.length; i += 2) { pl.push({ latitude: coorsArr[i], longitude: coorsArr[i + 1] }) } _this.setData({ latitude:pl[0].latitude, longitude:pl[0].longitude, polyline: [{ points: pl, color: '#FF0000DD', width: 4 }] }) }, fail: function (error) { console.error(error); }, complete: function (res) { console.log(res); } }); } })
</br>
## 接口调用说明
路线规划调用接口服务如下:
- /ws/direction/v1/driving 路线规划:驾车
- /ws/direction/v1/walking 路线规划:步行
- /ws/direction/v1/transit 路线规划:公交
- /ws/direction/v1/bicycling 路线规划:自行车
注:微信小程序JavaScript SDK通过对腾讯位置服务WebServiceAPI接口进行封装而形成,因此和直接调用WebSerivceAPI的限制是等同的,<br>具体可参考:[腾讯位置服务WebServiceAPI配额及使用限制](https://lbs.qq.com/dev/console/quotaImprove)
getCityList(options:Object)
获取全国城市列表数据。
注:坐标系采用gcj02坐标系
</br>
options属性说明
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| sig | String | 否 | 签名校验</br>开启WebServiceAPI签名校验的必传参数,只需要传入生成的SK字符串即可,不需要进行MD5加密操作</br>该参数适用于 jssdkv1.1 jssdkv1.2 |
调用结果
通过属性success, fail, complete的回调参数来接收调用结果
success的回调参数可以有2个,第1个参数接收调用结果,第2个参数控制返回处理后的数据(非必须参数),示例:success:function(res,data)
该属性适用于 jssdkv1.1 jssdkv1.2
回调结果对象
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | number | 是 | 状态码,0为正常,310请求参数信息有误,311Key格式错误,306请求有护持信息请检查字符串,110请求来源未被授权 |
| message | string | 是 | 状态说明,即对状态码status进行说明 |
| result | DistrictInfo[][] | 是 | 结果数组,第0项代表一级行政区划,第1项代表二级行政区划,以此类推 |
DistrictInfo 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 是 | 行政区划唯一标识 |
| name | string | 否 | 简称,如"内蒙古" |
| fullname | string | 是 | 全称,如"内蒙古自治区" |
| location | LatLngObject | 是 | 中心点坐标 |
| pinyin | string[] | 否 | 行政区划拼音,每一下标为一个字的全拼,如:["nei","meng","gu"] |
| cidx | number[] | 否 | 子级行政区划在下级数组中的下标位置 |
LatLngObject 对象属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| lat | number | 是 | 纬度 |
| lng | number | 是 | 经度 |
示例
Javascript 关键代码片段: ``` javascript // 引入SDK核心类 var QQMapWX = require('xxx/qqmap-wx.js');
// 实例化API核心类 var qqmapsdk = new QQMapWX({ key: '开发密钥(key)' // 必填 });
//在Page({})中使用下列代码 //页面显示/切入前台时触发 onShow: function() { var _this = this; //调用获取城市列表接口 qqmapsdk.getCityList({ success: function(res) {//成功后的回调 console.log(res); console.log('省份数据:', res.result[0]); //打印省份数据 console.log('城市数据:', res.result[1]); //打印城市数据 console.log('区县数据:', res.result[2]); //打印区县数据 }, fail: function(error) { console.error(error); }, complete: function(res) { console.log(res); } }); }
## 接口调用说明
getCityList(options:Object)方法调用接口服务如下:
- /ws/district/v1/list 行政区划:列表
注:微信小程序JavaScript SDK通过对腾讯位置服务WebServiceAPI接口进行封装而形成,因此和直接调用WebSerivceAPI的限制是等同的,<br>具体可参考:[腾讯位置服务WebServiceAPI配额及使用限制](https://lbs.qq.com/dev/console/quotaImprove)
MapContext.addArc(Object object)
基础库 2.22.0 开始支持,低版本需做兼容处理。
以 Promise 风格 调用:不支持
>
小程序插件:支持
>
微信 鸿蒙 OS 版:支持
相关文档: map
功能描述
添加弧线,途经点与夹角必须设置一个。途经点必须在起终点有效坐标范围内,否则不能生成正确的弧线,同时设置夹角角度时,以夹角角度为准。夹角定义为起点到终点,与起点外切线逆时针旋转的角度。工具侧暂未支持。
参数
Object object
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| id | number | 是 | 圆弧 id | |
| start | Object | 是 | 起始点 | |
| end | Object | 是 | 终点 | |
| pass | Object | 否 | 途经点 | |
| angle | number | 0 | 否 | 夹角角度 |
| width | number | 5 | 否 | 线宽 |
| color | number | #000000 | 否 | 线的颜色 |
| success | function | 否 | 接口调用成功的回调函数 | |
| fail | function | 否 | 接口调用失败的回调函数 | |
| complete | function | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) |
start 结构
| 结构属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| longitude | number | 是 | 经度 | |
| latitude | number | 是 | 纬度 |
end 结构
| 结构属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| longitude | number | 是 | 经度 | |
| latitude | number | 是 | 纬度 |
pass 结构
| 结构属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| longitude | number | 是 | 经度 | |
| latitude | number | 是 | 纬度 |
Related skills
FAQ
What does it need to run?
A TMAP_MINIPROGRAM_KEY; without a formal key it prompts to register one or use a limited experience key before doing anything else.
What map features does it cover?
Map component, markers, polylines, polygons, MapContext control, location services, routing, geocoding, POI search, and clustering.