来自 mapbox-vector-tile-java 的 Vector Tiles 导致在 mapbox-gl 中呈现异常高纬度的特征

Vector Tiles from mapbox-vector-tile-java results in features rendered in mapbox-gl at abnormally high latitudes

我们正在尝试使用 mapbox-vector-tile-java 将矢量切片端点合并到后端框架中,以将地理特征编码到矢量切片中。除了一些奇怪的行为外,我们大部分时间都在工作,在这些行为中,渲染的特征被放置在比它们应该的纬度高得多的地方。放大时,功能将跳到较低的纬度,直到最终在非常低的缩放级别下获得相当好的精度。例如,我的数据是新墨西哥州的县。在初始页面加载时,所有县都呈现在加拿大。当我放大到 2 级时,它们会跳到加拿大南部。我放大得越多,他们就越接近新墨西哥州的实际位置。有谁知道我为什么会看到这种行为?

一些高级细节:

  1. 数据库中的地理特征在 EPSG:4326。
  2. JTS 与 mapbox-vector-tile-java 结合使用来处理数据编码。
  3. 将基于 URL 的 xyz 瓦片位置转换为瓦片边界使用 Mapbox 用于此类转换的精确方程,如 MapBox 的 TileBelt 中所引用。

示例URL

https://localhost:8443/app/location/data?x=0&y=0&z=0&config={一些:json}

构建矢量切片的后端 Java 代码

  Envelope tileEnvelope = this.getTileBounds(x, y, zoom);

  GeometryFactory geomFactory = new GeometryFactory();
  IGeometryFilter acceptAllGeomFilter = geometry -> true;

  MvtLayerParams layerParams = new MvtLayerParams();      

  TileGeomResult tileGeom = JtsAdapter.createTileGeom(geometries, tileEnvelope, geomFactory, layerParams, acceptAllGeomFilter);

  final VectorTile.Tile.Builder tileBuilder = VectorTile.Tile.newBuilder();

  // Create MVT layer
  final MvtLayerProps layerProps = new MvtLayerProps();
  final IUserDataConverter ignoreUserData = new UserDataConverter();

  // MVT tile geometry to MVT features
  final List<VectorTile.Tile.Feature> features = JtsAdapter.toFeatures(tileGeom.mvtGeoms, layerProps, ignoreUserData);

  final VectorTile.Tile.Layer.Builder layerBuilder = MvtLayerBuild.newLayerBuilder(layerName, layerParams);
  layerBuilder.addAllFeatures(features);

  MvtLayerBuild.writeProps(layerBuilder, layerProps);

  // Build MVT layer
  final VectorTile.Tile.Layer layer = layerBuilder.build();

  // Add built layer to MVT
  tileBuilder.addLayers(layer);

  /// Build MVT
  Tile mvt = tileBuilder.build();

  return mvt.toByteArray();

将图块网格位置转换为图块边界的代码

public Envelope getTileBounds(int x, int y, int zoom)
{
    return new Envelope(this.getLong(x, zoom), this.getLong(x + 1, zoom), this.getLat(y, zoom), this.getLat(y + 1, zoom));
}

public double getLong(int x, int zoom)
{
    return ( x / Math.pow(2, zoom) * 360 - 180 );
}

public double getLat(int y, int zoom)
{
    double r2d = 180 / Math.PI;
    double n = Math.PI - 2 * Math.PI * y / Math.pow(2, zoom);
    return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
}

平铺网格转换结果

A URL with x=0,y=0,z=1 导致 Env[-180.0 : 0.0, 0.0 : 85.0511287798066] 这是一个如预期覆盖北美的图块(我们确定了有效性使用此网站:www.maptiler.org/google-maps-coordinates-tile-bounds-projection).

我们有一些预感(只是猜测)

  1. 瓦片网格位置到 mapbox-gl 矢量瓦片网格之间的转换与上面的方程不同,尽管我们使用了他们在 TileBelt 中也使用的方程。
  2. mapbox-vector-tile-java 提供的图块编码不正确。
  3. 我们错误地设置了 JTS 几何图形的边界。

当然,这是一个显而易见的解决方案,但不知何故我们没有早点发现。我们只需要在编码 Vector Tiles 之前将 JTS 功能投影到 EPSG 3857。