微信小程序添加外部地图服务数据

先上效果:

image

缘起

使用微信小程序做地图相关功能的时候,有个需求是需要接入自己发布的地图服务。查看微信小程序地图组件文档,发现它对地图相关的支持很少,只有一些基础功能,比如添加点、线、面、气泡和一些常规的地图事件监听,并没有添加地图服务相关的支持。

不过有了需求,也要想办法解决呀。

图层查询

既然小程序不能直接添加地图服务,那就把图层数据查出来,然后通过添加点线面方式添加到地图,具体要怎么实现呢?

首先想到的是通过图层查询接口把所有数据查出来。

但是既然数据是按图层发布的,一般数据量都比较大,把所有数据查询出来,一次性添加过多的数据到地图,地图组件会受不了从而变的卡顿,另外微信小程序单次setData()的数据不能超过1024kB,因此这种方案就不可取了。

矢量瓦片

既然一次性请求数据量太大,是不是可以分批次请求呢?于是就想到了矢量瓦片。
矢量瓦片对于做GIS的人来说,大家都很熟悉了,这也是目前各种GIS产品对大数据量地图展示所采用的主要方式。
但是,我们如何让不支持添加外部图层的小程序地图组件支持矢量瓦片呢?

查看地图组件相关文档,会看到其中有个regionchange事件,该事件是在地图视野改变,也就是拖动、缩放地图时触发,它会返回当前中心点、缩放级别、地图范围等信息。

image

获取瓦片

接下来就是如何根据这些参数获取到矢量瓦片了。

假设,地图切图的原点是(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,这是因为WMTSTMS两种方式调用切片时,传入的y值是不同的,不过两者之间是有可以转换的,也就是tmsY = Math.pow(2, (level - 1)) - y - 1WMTS用的是这里返回的y,TMS用的是这里返回 的tmsY

参考链接:

WebGIS前端地图显示之根据地理范围换算出瓦片行列号的原理(核心)

Slippy_map_tilenames

TMS和WMTS大概对比

接下来我们只需根据当前地图可视范围的最大、最小坐标以及地图层级,即可获取包含当前地图可视范围的瓦片的编号。

由于微信小程序地图组件使用的是国测局加密坐标,而我发布的地图服务数据为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
  })
},

存在问题

至此,微信小程序添加矢量瓦片数据已经完成,基本能满足浏览外部矢量图层的需求,但是,这里还是有一些不足的地方

  1. 需要发布geojson格式矢量瓦片图层
  2. 地图拖动时图层会闪一下,这是小程序重新往地图上绘制点线面图层引起的
  3. 在小比例尺瓦片返回数据量较大时可能会有卡顿现象(可以通过限定最小比例尺优化)
  4. 图层配图效果受小程序地图点线面样式限制

虽然该解决方案存在一些问题,但是鉴于微信小程序地图组件的限制,并且确时又有添加图层的需求,此方案还是可取的。

总结

  1. 微信小程序地图组件不支持添加外部图层服务
  2. 通过发布geojson格式矢量瓦片服务,然后按当前可视范围获取geojson格式瓦片数据
  3. 通过小程序地图组件的regionchange事件监听地图拖动、缩放,可以获取到当前中心点、缩放级别、地图范围
  4. 根据缩放级别、地图范围可以获取到当前可视范围的瓦片编号
  5. 请求瓦片数据,通过微信小程序地图组件中添加点线面的方法把切片数据添加到地图

代码地址

代码地址:http://gisarmory.xyz/blog/index.html?source=WechatVectorTile


原文地址:http://gisarmory.xyz/blog/index.html?blog=WechatVectorTile

欢迎关注《GIS兵器库》

本文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名《GIS兵器库》(包含链接: http://gisarmory.xyz/blog/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容