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

标签 leaflet react-leaflet

我正在尝试为 Leaflet 创建一个基于 Konva 的 GridLayer(基本上是围绕 Canvas 元素的抽象,以尝试有效地呈现数万个功能)。我有一些代码似乎在某种程度上有效(我的示例数据中的行似乎符合我的预期),但我遇到了奇怪的行为。具体来说,功能似乎可以明显地“传送”或完全消失。此外,在瓷砖边缘看到线条中断的情况并不少见。我怀疑这意味着我错误地计算了每个图 block 内的像素位置(尽管肯定有其他问题是错误的)。我基本上是在识别图 block 的像素位置(renderStage() 中的 x、y),并将 map 像素位置平移那么多像素(pt.x 和 pt.y,通过投影纬度/经度生成)。这旨在创建一个 [x1, y1, x2, y2, ...] 数组,可以将其渲染到单个图 block 。一切都应该在 EPSG:4326 中。

有谁知道如何正确地将纬度/经度投影到 GridLayer 的各个图 block 内的像素坐标?有很多针对整个 map 执行此操作的示例,但这似乎并没有清楚地转化为如何在从 map 左上角偏移的图 block 中找到那些相同的像素位置。

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);

最佳答案

Everything is expected to be in EPSG:4326.

没有。

处理栅格数据(图像图 block )后,所有内容都应该在 map 的显示 CRS 中(默认情况下为 EPSG:3857),或者以相对于 CRS 原点的像素为单位。这些概念在 one of Leaflet's tutorials 中有更深入的解释。 .

事实上,您似乎在这里以像素为单位工作,至少对于您的观点而言:

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

但是,您计算每个图 block 的像素偏移量太天真了:

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

那应该依赖于私有(private) 方法 _getTiledPixelBoundsL.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(....); }

另一方面:

[...] an abstraction around canvas elements to try and render tens of thousands of features efficiently

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

关于leaflet - 如何将纬度/经度转换为 GridLayer Tile 中的正确像素位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51901780/

相关文章:

javascript - 使用传单和 geoJson 监视对象

javascript - 如何在 Leaflet.js 中添加多个标记?

javascript - 将 Google 折线图放入 Leaflet 弹出窗口中

javascript - Leaflet.PolylineMeasure 在新环境中不起作用

javascript - React Leaflet 弹出窗口在移动设备上不起作用

javascript - React Leaflet : Dynamic Markers, 无法读取属性 'addLayer'

javascript - 向 map 添加新标记时传单抛出 "Uncaught TypeError: Cannot read property ' lat' of undefined"

javascript - 尝试使用 react-leaflet-choropleth : Cannot read property 'map' of undefined 映射等值线时出错

reactjs - 如何在 react 传单上覆盖内容(z-index问题)

javascript - 传单 (geojson) 多边形上的简单标签