需求
最近项目甲方想把PC端的地图线路绘制在手机上,并且还要在小程序上,听得我心里一惊,那么多线,那么多点符号,我该如何画啊,小程序最大setData也有1M的限制。但是需求来了就得想办法解决。
寻找方法
1、小程序方法polyline
+marker
(适用普通的点位渲染)
- 小程序文档翻了翻,发现map组件文档有个 “聚合能力” 我似乎看见了新大陆,觉得肯定可以满足,经过一番研究,还是不能满足,因为这种方法只能用
polyline
来画线,而点或符号用marker
来画,这种方法数据要处理大多了,因为我的项目线多,符号也多还不同形状的符号,PC端的显示是使用瓦片来渲染,没有分那么多符号和点位,这个方法以失败告终。有兴趣可以看下这个博客写的内容和我这里讲的差不多:微信小程序添加外部地图服务数据
2、自定义图层WMSLayer
- 上面方法不行,于是我找了gis,跟他讨论了我这个需求,看看有没有办法可以满足我的需求,他们就给我推荐了自定义图层WMSLayer,他说PC端就是用这种方式实现,导入一份数据就可以了。我就说好的,我自己回去研究了一番,发现是需要引入GL的包,但是小程序不支持dom操作,和普通web页面还是有区别,于是我就放弃了该方法。
3、Leaflet插件
- 这个方法也是gis那边介绍的一个插件库,我寻找了很多遍,发现没有支持小程序的插件库,只是支持web网页开发,还是无法使用到小程序中。传送门leaflet地址
4、小程序个性化图层
- 这个方法我研究过,就是可以直接导入图层瓦片,最终腾讯生成一个个性化地图id,然后小程序调用个性化图层id。该方法好像还行,但是我没有具体操作,因为我的数据不是固定的,随时会改变,如果每次改变又去改个性化地图就惨了,这种方法适用于景区景点类型。
- 腾讯地图个性化图层
- 看见社区有人发的web效果图地址
- 个性化地图使用指南
我使用的方法
- 思来索去既然瓦片是图,那能不能让gis那边直接返回一张图片呢?我这边就贴图就行?因为小程序刚好有个
自定义图片图层
可以跟着地图变大变小 - 感觉这个方法挺好的,联想了加入是一张张瓦片我也可以对准位置贴上去,连在一起就是我想要的效果了。于是找了gis那边的同事,发现他们有这种方法可以获取图片。addGroundOverlay文档地址
思路
- 先看下addGroundOverlay的参数
单独一个参数抽出来
- 由图可见,有一个很重要的参数
bounds
这个参数,这个参数就是获取地图的对角点,用来确定当前地图视野范围,有了这个参数,我们就叫gis那边生成一张图片,然后粘贴到我们小程序的地图上面就完成了渲染。这里需要注意几点:- gis那边生成的图片需要几个参数,
- a、需要对角点即
bounds
一样的参数,可以通过小程序提供的MapContext.getRegion获取当前地图的视野范围 - b、需要获取你当前手机全屏屏幕宽高(windowWidth这个),用这个宽高传给gis那边生成图片的宽高
- c、使用地图map组件提供的方法
bindregionchange
来监听,当前缩放或者中心点移动,然后去请求最新视野的图片粘贴上去 - d、这里有个不好的问题就是用
MapContext.updateGroundOverlay
方法,更新上一张的贴图会出现闪动问题(即更新最新的图片会发生显示原来图片位置、然后隐藏才显示最新的图片位置),为了解决这个问题,我使用了每次变动位置请求最新图片,都用增加方法MapContext.addGroundOverlay
去添加贴图到地图,然后把旧的贴图全部删除,为了效果看起来像懒加载,等加载完最新的图片在去删除旧的图片,这样就不会出现闪动的问题
核心代码如下:
- a、需要对角点即
- gis那边生成的图片需要几个参数,
onReady: function (options) {
this.MapContext = wx.createMapContext('mapId', this)
this.getSystemInfo = wx.getSystemInfo().then(res => {
this.getSystemInfo = res
})
this.indexId = 1
},
bindregionchange: function (e) {
if (e.type === 'end') {
const northeast = e.detail.region.northeast
const southwest = e.detail.region.southwest
if (mapTimer) clearTimeout(mapTimer)
mapTimer = setTimeout(()=> {
this.updataMap(southwest, northeast)
}, 100)
}
},
updataMap(southwest, northeast) {
const northeast1 = {
longitude: CoordTransformUtil.GCJ02ToWGS84(northeast.longitude, northeast.latitude)[0],
latitude: CoordTransformUtil.GCJ02ToWGS84(northeast.longitude, northeast.latitude)[1]
}
const southwest1 = {
longitude: CoordTransformUtil.GCJ02ToWGS84(southwest.longitude, southwest.latitude)[0],
latitude: CoordTransformUtil.GCJ02ToWGS84(southwest.longitude, southwest.latitude)[1]
}
overlayId.push(this.indexId)
this.indexId = this.indexId + 1
this.MapContext.addGroundOverlay({
id: this.indexId,
src: `https://xxx/export?bbox=${northeast1.longitude},${northeast1.latitude},${southwest1.longitude},${southwest1.latitude}&bboxSR=4490&layers=&layerDefs=&size=${this.getSystemInfo.windowWidth},${this.getSystemInfo.windowHeight}`,
bounds: {
southwest: southwest,
northeast: northeast
},
success: async (res) => {
// 由于使用updata图层会出现每次闪图渲染问题,所以每次获取图层都是新增的,故这里需要把每次渲染的图层删除,只留最后一次
if (mapTimer2) clearTimeout(mapTimer2)
mapTimer2 = setTimeout(async() => {
await Promise.all(overlayId.map(async (item) => {
await this.MapContext.removeGroundOverlay({ id: item });
overlayId = overlayId.filter(fil => fil != item)
}));
}, 500)
},
fail: (err) => {
console.error(err, 'fail...addGroundOverlay')
}
})
}
总结
抱歉了,其实我这是不讲武德,把技术点给gis那边处理了,我这边只需要把他们处理好的图片粘贴上去就好了,经过验证该方法确实可行的哦
解决问题才是王道
代码片段:https://developers.weixin.qq.com/s/taAR41mz7GHT
leaflet没有微信小程序版本,那就帮它搞个https://gitee.com/zz2022com/DTPlugin.git😄
好主意~后期会把单纯的地图组件从系统里独立出来,跟api解绑,就跟leaflet类似了
感谢分享,我回头也在项目里试试