Add external map service data to wechat applet

First up effect:

origin

When using wechat applet for map related functions, there is a need to access the published map service. View wechat applet Map components It is found that it has little support for map related functions, only some basic functions, such as adding points, lines, surfaces, bubbles and some conventional map event monitoring, and no support for map services.

However, if there is a demand, we should also find a way to solve it.

Layer query

Since the applet cannot directly add map services, find out the layer data, and then add it to the map by adding points, lines and surfaces. How to implement it?

The first thought is to find out all the data through the layer query interface.

However, since the data is published by layer, the amount of data is generally large. Query all the data and add too much data to the map at one time, and the map component will become stuck. In addition, the data of wechat applet setData() cannot exceed 1024kB at a single time, so this scheme is not desirable.

Vector tile

Since the amount of data requested at one time is too large, can it be requested in batches? So I thought of vector tiles.
Vector tiles are familiar to GIS people, which is also the main way for large data map display of various GIS products.
However, how can we make the applet map component that does not support adding external layers support vector tiles?

When you view the documents related to the map component, you will see a regionchange event. This event is triggered when the map view changes, that is, when you drag and zoom the map. It will return the current center point, zoom level, map range and other information.

Get tiles

The next step is how to obtain vector tiles according to these parameters.

Suppose that the origin of the map cut is (originX,originY), the tile size of the map is tile size, and the actual distance represented by 1 pixel on the map screen is resolution. The formula for calculating the row and column number of tiles where the coordinate point (x,y) is located is:

col = floor((x0 - x)/( tileSize*resolution))
row = floor((y0 - y)/( tileSize*resolution))

This formula should not be difficult to understand. To put it simply, first calculate the actual length LtileSize contained in a tile, then calculate the actual distance LrealSize between the geographic coordinate point on the screen and the starting point of the tile cut-off diagram, and then divide the actual distance by the actual length of a tile to obtain the tile row and column number at this time: LrealSize/LtileSize.

The specific codes are as follows:

getTileXY: function (lon, lat, level) {
  let originX = -180; //The value of x at the origin of the coordinate system,
  let originY = 90; //The value of y at the origin of the coordinate system
  //According to your corresponding slicing scheme, this is its resolution 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 //This value represents the size of each slice, usually 256
  let coef = resolution[level] * tileSize;
  let x = Math.floor((lon - originX) / coef); // Round down and discard the decimal part
  let y = Math.floor((originY - lat) / coef); // Round down and discard the decimal part
  let tmsY = Math.pow(2, (level - 1)) - y - 1;
  return {
    x: x,
    y: y,
    z: level - 1,
    tmsY: tmsY
  }
},

Here, I can see that there is a y value and a tmsY in the returned data. This is because when WMTS and TMS call slices, the incoming y value is different, but they can be converted, that is, tmsY = Math.pow(2, (level - 1)) - y - 1. WMTS uses the Y returned here and TMS uses the tmsY returned here.

Reference link:

The principle of converting tile row and column numbers according to geographical range in WebGIS front-end map display (core)

Slippy_map_tilenames

Approximate comparison between TMS and WMTS

Next, we only need to obtain the number of tiles containing the current map viewing range according to the maximum and minimum coordinates of the current map viewing range and the map level.

Since the wechat applet map component uses the encrypted coordinates of the National Survey Bureau, and the map service data I released is wgs84 coordinates, it is necessary to use the coordinate conversion method to convert the coordinates of the National Survey Bureau into wgs84 coordinates when obtaining the slice number. The coordinate correction method can be referred to How to gracefully solve the offset problem of Baidu and Gaode maps in leaflet.

getXYZList: function (region, level) {
  // coordinate transformation 
  var newsouthwest = appcoord.gcj02_To_gps84(region.southwest.longitude, region.southwest.latitude); 
  var northeastwest = appcoord.gcj02_To_gps84(region.northeast.longitude, region.northeast.latitude);
  // Get Tile Number
  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)
    }
  }
},

Then, the geo JSON format data of the corresponding vector slice can be obtained by passing in the request address and x, y and z parameters through wx.request

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)
    }
  })
},

Note that I use geoserver to publish vector tiles here. During the calling process, I found a problem. In the data returned by a point layer tile, each tile always has a lot of duplicate data. After inspection and test, it is found that this is because the style used when publishing the layer (point layer) is a 40x88 picture point sample, This causes a lot of pixel values to be buffered outward during image cutting. Therefore, if the layer published by geoserver is used for vector slice call, it is best to set the point layer style to a pixel of pixel size, which can effectively reduce tile data redundancy

Add data

Finally, the obtained slice data can be added to the map by adding points, lines and surfaces to the map component of wechat applet

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
  })
},

Existing problems

So far, adding vector tile data to wechat applet has been completed, which can basically meet the needs of browsing external vector layers. However, there are still some deficiencies here

  1. You need to publish vector tile layers in geojson format
  2. When dragging the map, the layer will flash, which is caused by the applet redrawing the point, line and surface layer on the map
  3. When the amount of data returned by small-scale tiles is large, there may be Caton phenomenon (which can be optimized by limiting the minimum scale)
  4. The effect of layer matching is limited by the point, line and surface style of applet map

Although there are some problems with this solution, it is still desirable in view of the limitations of wechat applet map components and the need to add layers.

summary

  1. The wechat applet map component does not support adding external layer services
  2. By publishing the geojson format vector tile service, and then obtain the geojson format tile data according to the current visual range
  3. Monitor map dragging and zooming through the regionchange event of the applet map component to obtain the current center point, zoom level and map range
  4. The tile number of the current visual range can be obtained according to the zoom level and map range
  5. Request tile data and add slice data to the map by adding points, lines and surfaces in the map component of wechat applet

Code address

Code address: http://gisarmory.xyz/blog/index.html?source=WechatVectorTile

Original address: http://gisarmory.xyz/blog/index.html?blog=WechatVectorTile

Welcome to pay attention< GIS weapon warehouse>

This article adopts Knowledge sharing Attribution - non-commercial use - sharing in the same way 4.0 international license agreement Permission. You are welcome to reprint, use and republish, but be sure to keep the signed GIS weapon Library (including link: http://gisarmory.xyz/blog/ ), shall not be used for commercial purposes, and the works modified based on this article must be distributed under the same license.

Discussion]( https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh )Permission. You are welcome to reprint, use and republish, but be sure to keep the signed GIS weapon Library (including link: http://gisarmory.xyz/blog/ ), shall not be used for commercial purposes, and the works modified based on this article must be distributed under the same license.

Tags: Mini Program gis

Posted on Tue, 26 Oct 2021 02:51:53 -0400 by Sergey Popov