将(旋转的)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),或任何同态坐标系统(相对于屏幕的像素坐标,相对于图层原点的像素坐标),您没问题的。
在阅读了 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),或任何同态坐标系统(相对于屏幕的像素坐标,相对于图层原点的像素坐标),您没问题的。