网站搜索

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


在阅读了Projection的文档,并进行了一些失败与尝试的测试后,我发现自己仍然无法找到这个问题的解决方案。

我找到的所有解决方案都利用了x和y坐标是以墨卡托地图投影为参考的这个事实,但在这种情况下,我的坐标来自放置在地图图层上的本地图像。

假设我有一张 400px * 200px 的灰色图片,通过 ImageOverlay.Rotated 放置在 leaflet 地图上,位置是:

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

这张图片在地图上的位置是使用左上、右上和左下坐标作为参考。

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

如果我知道我把手机放在像素 100,50(原始图像的左上角),我怎样才能将这个笛卡尔坐标转换为地图上显示的经纬度?

如果它是一个完美对齐于北方的矩形,我想我可以得到左上角和左下角之间的度数差,将其除以像素数,然后将结果乘以我的手机的Y值以获取纬度。使用之前的值,我会将其乘以我的手机的X值以获取手机的经度。

但是鉴于形状是旋转的,世界不是平的,而且我地理和三角学都很差,我不确定它是否足够精确。

归根结底,我们谈论的是一部手机,而不是一栋建筑。相差几米(2-4米)没问题,差一整个建筑街区就不行。

答案#1

哦,你是在说我自己的 Leaflet.ImageOverlay.Rotated。我很乐意照办。

你应该看看它的源代码。所有的数学运算都在那里。让我们过一下那段代码的一些部分:

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

这是将三个经纬度坐标转换为相对于屏幕的像素坐标。Leaflet 关于创建 L.Layer 子类的教程对“图层点”的含义有很好的解释。

您可以将这些计算替换为任何其他重投影,只要假设您是在平面上工作,而不是在地球椭球体上。换句话说:您可能想将您的经纬度坐标转换为 EPSG:3857 球面墨卡托坐标(这是 Leaflet 使用的显示投影)。如果您这样做了,之后可以将插值后的 EPSG:3857 坐标转换为 EPSG:4326 “普通经纬度”坐标。

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

CSS 转换需要倾斜角度来正确地扭曲图像。这可能违反直觉,但 ImageOverlay.Rotated 使用的是 CSS 的 skew() 而不是 rotate。但你不需要倾斜角度,你只需要那些差分向量。

如果你想可视化它,vectorX 是沿图像上边(从左到右)的二维向量,而 vectorY 是沿左边(从上到下)的二维向量。

这些向量允许你获取图像中任意点的屏幕坐标,前提是输入范围为 ([0..1], [0..1])(0 表示图像的顶部或左边,1 表示底部或右边)。但你可能不想使用相对于图像高度/宽度的数字来操作,因为你在问题中提到了像素。所以让我们来获得像素坐标。

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

这是从图像左上角到图像中 (xPixelCoordinate, yPixelCoordinate) 像素的屏幕坐标向量。

现在添加图像左上角的屏幕坐标,然后就可以了:

    var finalCoordinateForImagePixel = pxTopLeft.add(deltaVector);

当我们使用 latLngToLayerPoint 时,结果将相对于该参考框架。想要把它转换回来吗?很简单:

    var finalCoordinateForImagePixelInLatLong = 
           map.layerPointToLatLng(finalCoordinateForImagePixel);

但是鉴于形状是旋转的,世界不是平的,而且我地理和三角学都非常差,我不确定它是否足够精确。

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