微信小程序添加外部地图服务数据
先上效果:

缘起
使用微信小程序做地图相关功能的时候,有个需求是需要接入自己发布的地图服务。查看微信小程序地图组件文档,发现它对地图相关的支持很少,只有一些基础功能,比如添加点、线、面、气泡和一些常规的地图事件监听,并没有添加地图服务相关的支持。
不过有了需求,也要想办法解决呀。
图层查询
既然小程序不能直接添加地图服务,那就把图层数据查出来,然后通过添加点线面方式添加到地图,具体要怎么实现呢?
首先想到的是通过图层查询接口把所有数据查出来。
但是既然数据是按图层发布的,一般数据量都比较大,把所有数据查询出来,一次性添加过多的数据到地图,地图组件会受不了从而变的卡顿,另外微信小程序单次setData()的数据不能超过1024kB,因此这种方案就不可取了。
矢量瓦片
既然一次性请求数据量太大,是不是可以分批次请求呢?于是就想到了矢量瓦片。
矢量瓦片对于做GIS的人来说,大家都很熟悉了,这也是目前各种GIS产品对大数据量地图展示所采用的主要方式。
但是,我们如何让不支持添加外部图层的小程序地图组件支持矢量瓦片呢?
查看地图组件相关文档,会看到其中有个regionchange事件,该事件是在地图视野改变,也就是拖动、缩放地图时触发,它会返回当前中心点、缩放级别、地图范围等信息。

获取瓦片
接下来就是如何根据这些参数获取到矢量瓦片了。
假设,地图切图的原点是(originX,originY),地图的瓦片大小是tileSize,地图屏幕上1像素代表的实际距离是resolution。计算坐标点(x,y)所在的瓦片的行列号的公式是:
col = floor((x0 - x)/( tileSize*resolution))
row = floor((y0 - y)/( tileSize*resolution))
这个公式应该不难理解,简单点说就是,先算出一个瓦片所包含的实际长度LtileSize,然后再算出此时屏幕上的地理坐标点离瓦片切图的起始点间的实际距离LrealSize,然后用实际距离除以一个瓦片的实际长度,即可得此时的瓦片行列号:LrealSize/LtileSize。
具体代码如下:
getTileXY: function (lon, lat, level) {
let originX = -180; //坐标系原点的x的值,
let originY = 90; //坐标系原点的y的值
//根据你自己对应的切片方案改,这个就是其分辨率resolution
let resolution = [1.40625, 0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625,
0.010986328125, 0.0054931640625, 0.00274658203125, 0.001373291015625, 0.0006866455078125, 0.0003433227539062,
0.0001716613769531, 0.0000858306884766, 0.0000429153442383, 0.0000214576721191, 0.0000107288360596,
0.0000053644180298, 0.0000026822090149, 0.0000013411045074, 0.0000006705522537, 0.0000003352761269
]
let tileSize = 256 //这个值表示的是每张切片的大小,一般都是256
let coef = resolution[level] * tileSize;
let x = Math.floor((lon - originX) / coef); // 向下取整,丢弃小数部分
let y = Math.floor((originY - lat) / coef); // 向下取整,丢弃小数部分
let tmsY = Math.pow(2, (level - 1)) - y - 1;
return {
x: x,
y: y,
z: level - 1,
tmsY: tmsY
}
},
这里可以看到我返回的数据中有一个y值,还有一个
tmsY,这是因为WMTS、TMS两种方式调用切片时,传入的y值是不同的,不过两者之间是有可以转换的,也就是tmsY = Math.pow(2, (level - 1)) - y - 1,WMTS用的是这里返回的y,TMS用的是这里返回 的tmsY。参考链接:
接下来我们只需根据当前地图可视范围的最大、最小坐标以及地图层级,即可获取包含当前地图可视范围的瓦片的编号。
由于微信小程序地图组件使用的是国测局加密坐标,而我发布的地图服务数据为wgs84坐标,因此这里在获取切片编号时需要用坐标转换方法将国测局坐标转成wgs84坐标,坐标纠偏方法可参考leaflet中如何优雅的解决百度、高德地图的偏移问题。
getXYZList: function (region, level) {
// 坐标转换
var newsouthwest = appcoord.gcj02_To_gps84(region.southwest.longitude, region.southwest.latitude);
var northeastwest = appcoord.gcj02_To_gps84(region.northeast.longitude, region.northeast.latitude);
// 获取瓦片编号
var xyzInfo1 = this.getTileXY(newsouthwest.lng, northeastwest.lat, level)
var xyzInfo2 = this.getTileXY(northeastwest.lng, newsouthwest.lat, level)
var z = level - 1
for (var x = xyzInfo1.x; x <= xyzInfo2.x; x++) {
for (var y = xyzInfo1.y; y <= xyzInfo2.y; y++) {
this.getGeoJson(x, y, z)
}
}
},
然后通过wx.request传入请求地址以及x、y、z参数,即可获取到对应矢量切片的geojson格式数据
getGeoJson: function (x, y, z) {
const v = this
wx.request({
url: "http://127.0.0.1:7000/geoserver/gwc/service/wmts/rest/test:test/EPSG:4326/EPSG:4326:" +
z + "/" + y + "/" + x + "?format=application/json;type=geojson",
method: 'get',
success(res) {
var tileId = 'tile-' + x + '-' + y + '-' + z
tileData[tileId] = {
tileId: tileId,
features: []
}
if(res.statusCode === 200){
tileData[tileId].features = res.data.features
}
v.addFeatures(tileId)
}
})
},
注意,这里我是用
geoserver发布的矢量瓦片,在调用过程中发现个问题,其中一个点图层瓦片返回的数据中,各个瓦片总有很多重复数据,经检查测试发现,这是由于发布该图层(点图层)时使用的样式为一张大小为40x88的图片点样,这就导致切图时整体向外缓冲了不少的像素值,所以,如果geoserver发布的图层是用于矢量切片调用,最好将点图层样式设置为一个像素大小的像素点,这样可以有效减少瓦片数据冗余
添加数据
最后再通过微信小程序地图组件中添加点线面的方法把获取切片数据添加到地图即可
addFeatures: function (tileId) {
var polylines = this.data.polylines
var markers = this.data.markers
tileData[tileId].features.forEach(feature => {
if (feature.geometry.type === 'LineString') {
polylines.push(this.getPolyline(feature.geometry.coordinates, tileId))
} else if (feature.geometry.type === 'Point') {
markers.push(this.getMarker(feature.geometry.coordinates, tileId))
}
});
this.setData({
polylines: polylines,
markers: markers
})
},
存在问题
至此,微信小程序添加矢量瓦片数据已经完成,基本能满足浏览外部矢量图层的需求,但是,这里还是有一些不足的地方
- 需要发布
geojson格式矢量瓦片图层 - 地图拖动时图层会闪一下,这是小程序重新往地图上绘制点线面图层引起的
- 在小比例尺瓦片返回数据量较大时可能会有卡顿现象(可以通过限定最小比例尺优化)
- 图层配图效果受小程序地图点线面样式限制
虽然该解决方案存在一些问题,但是鉴于微信小程序地图组件的限制,并且确时又有添加图层的需求,此方案还是可取的。
总结
- 微信小程序地图组件不支持添加外部图层服务
- 通过发布
geojson格式矢量瓦片服务,然后按当前可视范围获取geojson格式瓦片数据 - 通过小程序地图组件的
regionchange事件监听地图拖动、缩放,可以获取到当前中心点、缩放级别、地图范围 - 根据缩放级别、地图范围可以获取到当前可视范围的瓦片编号
- 请求瓦片数据,通过微信小程序地图组件中添加点线面的方法把切片数据添加到地图
代码地址
代码地址:http://gisarmory.xyz/blog/index.html?source=WechatVectorTile
原文地址:http://gisarmory.xyz/blog/index.html?blog=WechatVectorTile
欢迎关注《GIS兵器库》

本文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名《GIS兵器库》(包含链接: http://gisarmory.xyz/blog/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。
微信小程序添加外部地图服务数据的更多相关文章
- 微信小程序-基于高德地图API实现天气组件(动态效果)
微信小程序-基于高德地图API实现天气组件(动态效果) 在社区翻腾了许久,没有找到合适的天气插件.迫不得已,只好借鉴互联网上的web项目,手动迁移到小程序中使用.现在分享到互联网社区中,帮助后续有 ...
- 微信小程序添加悬浮在线客服会话按钮
微信为小程序提供客服消息能力,小程序用户可以方便快捷地与小程序服务提供方进行沟通,并且已经做成了组件的形式,直接就可以调用.客服会话按钮,用于在页面上显示一个客服会话按钮,用户点击该按钮后会进入客服会 ...
- 微信小程序个人/企业开放服务类目一览表
微信小程序个人/企业开放服务类目一览表 微信小程序个人开放服务类目表 服务类目 类目分类一 类目分类二 引导描述 出行与交通 代驾 / / 生活服务 家政.丽人.摄影/扩印.婚庆服务.环保回收/废 ...
- 图解微信小程序---添加tabBar底部菜单,添加已做好轮播图操作
图解微信小程序---添加tabBar底部菜单,添加已做好轮播图操作 什么是tabBar? 顶部或者底部tab栏如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以 ...
- 为苹果ATS和微信小程序搭建 Nginx + HTTPS 服务
昨天测试开发微信小程序,才发现微信也要求用HTTPS加密数据,想来是由于之前苹果的ATS审核政策的缘故吧,微信想在苹果上开放小程序必然也只能要求开发者必须使用HTTPS了,于是在服务器上测试安装Ngi ...
- 微信-小程序-开发文档-服务端-模板消息:templateMessage.getTemplateList
ylbtech-微信-小程序-开发文档-服务端-模板消息:templateMessage.getTemplateList 1.返回顶部 1. templateMessage.getTemplateLi ...
- 微信-小程序-开发文档-服务端-模板消息:templateMessage.getTemplateLibraryById
ylbtech-微信-小程序-开发文档-服务端-模板消息:templateMessage.getTemplateLibraryById 1.返回顶部 1. templateMessage.getTem ...
- 微信-小程序-开发文档-服务端-模板消息:templateMessage.addTemplate
ylbtech-微信-小程序-开发文档-服务端-模板消息:templateMessage.addTemplate 1.返回顶部 1. templateMessage.addTemplate 本接口应在 ...
- 微信小程序腾讯地图SDK使用方法
一.本篇文章主要知识点有以下几种: 1.授权当前位置 2.map组件的使用 3.腾讯地图逆地址解析 4.坐标系的转化 二.效果如下: 三.WXML代码 <map id="map&quo ...
随机推荐
- 这些解决 Bug 的套路,你都会了不?
最近整理了我原创的 140 篇编程经验和技术文章,欢迎大家阅读,一起成长!指路:https://t.1yb.co/ARnD 大家好,我是鱼皮. 学编程的过程中,我们会遇到各式各样的 Bug,也常常因为 ...
- 01_初识C语言
第一章 - 初识C语言 基本了解C语言的基础知识,对C语言有一个大概的认识. 每个知识点就是简单认识,不做详细讲解. 1. 什么是C语言? C语言是一门通用计算机编程语言,广泛应用于底层开发.C语言的 ...
- 利用滴答定时器(SysTick)实现简单的延时函数
预备知识: 对标准库来说,如果定义了时钟频率,则系统会默认初始化该时钟频率. SysTick是CM4的内核外设,是一个24位的向下递减计数器,每次计数时间是1/SYSCLK,即1/168000000. ...
- centos7.0 能ping通ip 无法ping通域名处理方法
第一步: 检查 vi /etc/sysconfig/network-scripts/ifcfg-eth0 查看网卡配置里的dns是否与 vi /etc/resolv.conf 的 nameser ...
- python学习1-博客-DB操作类
#学习python,准备写一个博客,第一天:在别人代码基础上写一个数据库操作的db.py1)python代码 #!/usr/bin/env python # -*- coding: UTF-8 -*- ...
- P5494-[模板]线段树分裂
正题 题目链接:https://www.luogu.com.cn/problem/P5494 题目大意 给出一个可重集合要求支持 将集合\(p\)中在\([l,r]\)的数放到一个新的集合中 将集合\ ...
- CF573D-Bear and Cavalry【动态dp】
正题 题目链接:https://www.luogu.com.cn/problem/CF573D 题目大意 给出\(n\)个人\(n\)匹马,每个人/马有能力值\(w_i\)/\(h_i\). 第\(i ...
- Python setattr() 函数 ,Python super() 函数: Python 内置函数 Python 内置函数
描述 setattr 函数对应函数 getatt(),用于设置属性值,该属性必须存在. 语法 setattr 语法: setattr(object, name, value) 参数 object -- ...
- Python技法-序列拆分
Python中的任何序列(可迭代的对象)都可以通过赋值操作进行拆分,包括但不限于元组.列表.字符串.文件.迭代器.生成器等. 元组拆分 元组拆分是最为常见的一种拆分,示例如下: p = (4, 5) ...
- js高阶
1. 面向对象编程介绍 1.1 两大编程思想 --- 面向过程 --- 面向对象 1.2 面向过程编程 POP 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候在一 ...