【问题标题】:Transform X and Y coordinates inside a (rotated) ImageOverlay to latitude and longitude将(旋转的)ImageOverlay 内的 X 和 Y 坐标转换为纬度和经度
【发布时间】:2018-03-07 12:29:17
【问题描述】:

在阅读了 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);

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

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

如果我知道我把手机放在像素 100,50( 原始图像),我如何将这个笛卡尔坐标转换为纬度和经度以供我的地图显示?

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

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

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

【问题讨论】:

  • 随着图像的“分辨率”变大,这变得更加困难。由于地球的曲率,纬度和经度不是线性的(就像您的图像那样)。这是使用半正弦公式处理的。不确定这是否有帮助,但请查看该公式。它可能会帮助您找到答案。
  • 源图像非常小(最多只能表示几十米),因此图像的分辨率应始终保持不变。为了使它更容易,我只需要根据通用谷歌地图/传单缩放措施在缩放级别 25-30 的确切位置。

标签: javascript angular typescript leaflet angular-leaflet-directive


【解决方案1】:

哦,你说的是我自己的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);

这是将三个经纬度坐标转换为屏幕相对像素坐标。 Leaflet tutorial about creating subclasses of 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's skew() 而不是rotate。但是你不需要倾斜角度,你只需要那些微分向量。

如果您想将其可视化,vectorX 是沿着图像顶部(从左到右)的 2D 向量,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),或任何同态坐标系(相对于屏幕的像素坐标,相对于图层原点的像素坐标),您将没有问题.

【讨论】:

  • xPixelCoordinate 代表什么?它是一个传单点吗?我试图弄清楚 scaleBy 的来源。顺便说一句,答案非常棒。
  • 我设法输出了结果,但它似乎不适合图像。甚至位置 1,1 似乎也不行。也许我也应该倾斜结果坐标?
  • 正确。当我的图像旋转时,坐标是从正确的点(图像的左上角)计算出来的,但它们在纯垂直或水平轴上进行转换,与图像的轴无关。
  • 所以如果我想在图像的像素 601x、359y 上放置一个标记,它应该从原始分辨率缩放到显示,然后在 X 和 Y 的基础上进行计算角度是“倾斜”成一个角度,而不是纯粹的垂直和水平,对吧?
  • 现在我得到的是这样的:i.imgur.com/Y963K5V.png 小红点是像素 601、359 应该在的位置,标记是计算的结果。
猜你喜欢
  • 2016-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-29
  • 1970-01-01
  • 1970-01-01
  • 2011-12-05
相关资源
最近更新 更多