如何将经纬度转换为GridLayer Tile中正确的像素位置

时间:2018-08-17 19:27:43

标签: leaflet react-leaflet

我正在为Leaflet创建一个基于Konva的GridLayer(基本上是围绕画布元素的一种抽象,试图有效地渲染数以万计的功能)。我有一些似乎在某种程度上可以工作的代码(示例数据中的行似乎与我期望的一致),但是我的行为越来越奇怪。具体而言,功能似乎将明显“传送”或完全消失。此外,在图块的边缘看到断线并不少见。我怀疑这意味着我错误地计算了每个图块中的像素位置(尽管肯定有其他问题是可能的)。我基本上是在确定图块的像素位置(renderStage()中的x,y),然后将地图像素位置平移那么多像素(通过投影纬度/经度生成的pt.x和pt.y)。这旨在创建[x1,y1,x2,y2,...]的数组,可以将其渲染到单个图块。预计所有内容都将在EPSG:4326中。

有人知道如何正确地将lat / lon投影到GridLayer的各个图块中的像素坐标吗?对于整个地图,有很多示例可以做到这一点,但这似乎并不能完全转化为如何在偏离地图左上角的图块中找到相同的像素位置。

import { GridLayer, withLeaflet } from "react-leaflet";
import { GridLayer as LeafletGridLayer } from "leaflet";
import { Stage, Line, FastLayer } from "konva";
import * as Util from 'leaflet/src/core/Util';
import _ from "lodash";

export const CollectionLayer = LeafletGridLayer.extend({
  options: {
    tileSize: 256
  },
  initialize: function(collection, props) {
    Util.setOptions(this, props)
    this.collection = collection;
    this.stages = new Map();
    this.shapes = {};
    this.cached = {};
    this.on('tileunload', (e) => {
      const stage = this.stages[e.coords]
      if (stage) {
        this.stages.delete(e.coords)
        stage.destroy()
      }
    })
  },
  renderStage: function(stage, coords, tileBounds) {
    const x = coords.x * this._tileSize.x
    const y = coords.y * this._tileSize.y
    const z = coords.z;
    const layer = stage.getLayers()[0]

    if (!layer || !tileBounds) return;
    _.each(this.collection.data, (entity, id) => {
      if (entity.bounds && tileBounds.intersects(entity.bounds)) {
        let shape = this.shapes[id]
        if (!shape) {
          shape = new Line()
          shape.shadowForStrokeEnabled(false)
          this.shapes[id] = shape
        }
        layer.add(shape);
        const points = entity.position.reduce((pts, p) => {
          const pt = this._map.project([p.value[1], p.value[0]], this._tileZoom)
          pts.push(pt.x - x);
          pts.push(pt.y - y);
          return pts
        }, [])
        shape.points(points);
        shape.stroke('red');
        shape.strokeWidth(2);
        this.shapes[id] = shape
      }
    })
    layer.batchDraw()
  },
  createTile: function(coords) {
    const tile = document.createElement("div");
    const tileSize = this.getTileSize();
    const stage = new Stage({
      container: tile,
      width: tileSize.x,
      height: tileSize.y
    });
    const bounds = this._tileCoordsToBounds(coords);
    const layer = new FastLayer();
    stage.add(layer);
    this.stages[coords] = stage
    this.renderStage(stage, coords, bounds);
    return tile;
  }
});

class ReactCollectionLayer extends GridLayer {
  createLeafletElement(props) {
    console.log("PROPS", props);
    return new CollectionLayer(props.collection.data, this.getOptions(props));
  }
  updateLeafletElement(fromProps, toProps) {
    super.updateLeafletElement(fromProps, toProps);
    if (this.leafletElement.collection !== toProps.collection) {
      this.leafletElement.collection = toProps.collection
      this.leafletElement.redraw();
    }
  }
}

export default withLeaflet(ReactCollectionLayer);

1 个答案:

答案 0 :(得分:0)

  

所有内容都应包含在EPSG:4326中。

否。

一旦处理了栅格数据(图像图块),所有内容都应该在地图的显示CRS(默认情况下为EPSG:3857)中,或者相对于CRS原点以像素为单位。在one of Leaflet's tutorials中对这些概念进行了更深入的说明。

实际上,您似乎在这里以像素为单位,至少在您的观点上如此:

const pt = this._map.project([p.value[1], p.value[0]], this._tileZoom)

但是,您对每个图块的像素偏移的计算过于幼稚:

const x = coords.x * this._tileSize.x
const y = coords.y * this._tileSize.y

这应该依赖于_getTiledPixelBounds private 方法L.GridLayer,例如:

const tilePixelBounds = this._getTiledPixelBounds();
const x = tilePixelBounds.min.x;
const y = tilePixelBounds.min.y;

并使用这些界限在循环遍历这些点时添加一些健全性检查:

const pt = this._map.project([p.value[1], p.value[0]], this._tileZoom);
if (!tilePixelBounds.contains(pt)) { console.error(....); }

另一方面:

  

[...]围绕画布元素的抽象,试图有效地渲染成千上万的特征

我不认为使用Konva在<canvas>上实际绘制项目会提高性能-方法与Leaflet所使用的方法相同(而且,如果我们谈论的是平铺矢量数据, Leaflet.VectorGrid使用的相同)。无论顶层的库是什么,一万次调用canvas draw函数都将花费相同的时间。如果您有时间考虑其他选择,Leaflet.GLMarkers及其WebGL呈现可能会以兼容性较差和集成成本较高的价格提供更好的性能。