将(旋转的)ImageOverlay 内的 X 和 Y 坐标转换为纬度和经度

Transform X and Y coordinates inside a (rotated) ImageOverlay to latitude and longitude

在阅读了 Projection 的 documentation 和一些试错测试后,我发现自己无法找到解决此问题的方法。

我找到的所有解决方案都与 x 和 y 坐标参考墨卡托地图投影这一事实有关,但在这种情况下,我从放置在地图上图层上的本地图像中获取它们。

假设我有一张 400px * 200px 的灰度图像,使用 ImageOverlay.Rotated 放置在传单地图上,位置为:

var topleft = L.latLng(41.604766, 0.606402);
var topRight = L.latLng(41.605107, 0.606858);
var bottomleft = L.latLng(41.604610, 0.606613);

此图片是使用左上角、右上角和左下角坐标作为参考放置在地图上的。

结果是一个略微向北旋转的矩形。

如果我知道我把 phone 留在像素 100,50(左上角 原始图像),我如何将这个笛卡尔坐标转换为纬度和经度以便我的地图显示它?

如果它是一个完全与北对齐的矩形,我想我可以得到左上角和左下角之间的度差,用它除以像素数,然后将结果乘以我的 phone's Y 获取纬度。使用先前的值,我将它乘以我的 phone 的 X 得到我的 phone 的经度。

但是考虑到形状是旋转的,世界不是平的,我的地理和三角学真的很差,我不确定它是否足够精确。

最后我们说的是 phone,而不是建筑物。离几米(2-4)可以,隔一个街区就不行了。

哦,你说的是我自己的 Leaflet.ImageOverlay.Rotated。我很乐意效劳。

你应该看看它的 source code。所有的数学都在那里。让我们回顾一下该代码的一些部分:

    var pxTopLeft    = this._map.latLngToLayerPoint(this._topLeft);
    var pxTopRight   = this._map.latLngToLayerPoint(this._topRight);
    var pxBottomLeft = this._map.latLngToLayerPoint(this._bottomLeft);

这是将三个 lat-long 坐标转换为 screen-relative 像素坐标 Leaflet tutorial about creating subclasses of L.Layer 很好地解释了 "layer point" 的含义。

您可以将这些计算替换为您想要的任何其他重投影,假设您在平面上而不是在大地水准面上工作。换句话说:您可能希望将 lat-long 坐标转换为 EPSG:3857 球面墨卡托坐标(这是 Leaflet 使用的显示投影)。如果这样做,您稍后可以将插值的 EPSG:3857 坐标转换为 EPSG:4326 "plain latitude-longitude" 坐标。

    // Calculate the skew angles, both in X and Y
    var vectorX = pxTopRight.subtract(pxTopLeft);
    var vectorY = pxBottomLeft.subtract(pxTopLeft);

CSS 变换需要倾斜角度才能正确扭曲图像。它可能是 counter-intuitive,但 ImageOverlay.Rotated 使用 CSS's skew() rather than rotate。但是你不需要倾斜角度,你只需要那些微分向量。

如果您想将其可视化,vectorX 是沿着图像顶部(从左到右)的二维矢量,vectorY 是沿着图像顶部的二维矢量左侧(从上到下)。

假设输入在 ([0..1], [0..1]) 范围内(0 为顶部或图像的左侧,1 表示底部或右侧)。但是您可能不想使用相对于图像 height/width 的数字,因为在您的问题中您提到了像素。所以让我们抓住像素。

    var imgW = this._rawImage.width;
    var imgH = this._rawImage.height;

很好。我们已经从 ImageOverlay.Rotated 中提取了所有需要的数学知识。


现在,如果将前面的向量除以以像素为单位的图像尺寸...

    var vectorXperPx = vectorX.divideBy(imgW);
    var vectorYperPx = vectorY.divideBy(imgW);

这些向量从原始图像的一个像素到其左侧 (x) 或下方 (y) 的像素。因此给定原始图像的像素 (x,y),相对于图像角点的投影向量将是:

    var deltaVector = 
               xPixelCoordinate.scaleBy(vectorXPerPx)
          .add(yPixelCoordinate.scaleBy(vectorYPerPx))

这是屏幕坐标中从图像的 top-left 角到图像的 (xPixelCoordinate, yPixelCoordinate) 像素的矢量。

现在添加图像 top-left 角的屏幕坐标,您就设置好了:

    var finalCoordinateForImagePixel = pxTopLeft.add(deltaVector);

正如我们使用的 latLngToLayerPoint,结果将相对于该参考系。想拿回来吗?简单:

    var finalCoordinateForImagePixelInLatLong = 
           map.layerPointToLatLng(finalCoordinateForImagePixel);

but given the shape is rotated, the world is not flat, and i was really bad at geography and trigonometry, i'm not sure about it being precise enough.

如果您使用 Leaflet 用于显示的坐标参考系统 (EPSG:3857),或任何同态坐标系统(相对于屏幕的像素坐标,相对于图层原点的像素坐标),您没问题的。