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),或者任何同构的坐标系统(相对于屏幕的像素坐标,相对于图层原点的像素坐标),你将没有问题。
