从Google静态地图中的像素坐标获取经纬度

9
我有一个JAVA项目,涉及Google静态地图,在花费了数小时的工作后,我无法让它正常运行。我会解释一切,并希望有人能帮助我。
我使用了一个静态地图(480像素×480像素),地图的中心是lat=47,lon=1.5,缩放级别为5。
现在,我需要在点击此静态地图上的像素时能够获得lat和lon。经过一些搜索,我发现应该使用Mercator投影(对吗?),我还发现每个缩放级别都会使水平和垂直维度的精度加倍,但我无法找到正确的公式来将像素、缩放级别和lat/lon联系起来...
我的问题只是从像素中获取lat/lon,知道中心坐标和像素以及缩放级别...
谢谢您提前!

你有学习墨卡托投影的页面链接吗? - Sean Mickey
4个回答

2
请使用墨卡托投影。
如果您将其投影到一个[0, 256)[0,256]的空间中:
LatLng(47,=1.5) is Point(129.06666666666666, 90.04191318303863)

在缩放级别为5时,这些等同于像素坐标:
x = 129.06666666666666 * 2^5 = 4130
y = 90.04191318303863 * 2^5 = 2881

因此,您的地图左上方位置为:
x = 4130 - 480/2 = 4070
y = 2881 - 480/2 = 2641

4070 / 2^5 = 127.1875
2641 / 2^5 = 82.53125

最后:

Point(127.1875, 82.53125) is LatLng(53.72271667491848, -1.142578125)

谢谢您的回复,但我认为有些不对。您说我的左上角在LatLng(53.72271667491848,-1.142578125),这是在英国,但实际上,我的地图左上角更接近爱尔兰,请看:http://i50.tinypic.com/9vb2b7.jpg您可以看到我的窗口,
  • 地图以LatLng(47,1.5)为中心
  • 地图大小为480x480
  • 地图缩放级别为5 --> 所以我猜LatLng(47,1.5)== Point(240,240),对吗?(当我将鼠标放在Point(240,240)上时,我离LatLng(47,1.5)很近)
提前谢谢
- user1374021
@user1374021:虽然这是一个旧的串,但我正在解决这个问题,这个答案对我很有帮助。问题出在计算x的数学上;应该是x = 4130 - 480/2 = 3890。y = 2641是正确的。当你输入这些数字后,你会得到一个-9.00的值,与你的图像相匹配。 - 4mla1fn

1
Google Maps使用瓦片来将世界高效地分成一个256^21像素瓦片的网格。基本上,最低缩放级别下的世界由4个瓦片组成。当您开始缩放时,您会得到16个瓦片,然后是64个瓦片,最后是256个瓦片。它基本上是四叉树。因为这样的一维结构只能展平二维,所以您还需要一个墨卡托投影或转换为WGS 84。这里有一个很好的资源将经纬度转换为给定图片上的像素x/y。Google Maps中有一个函数可以将纬度-经度对转换为像素。这里有一个链接,但它说瓦片只有128x128:http://michal.guerquin.com/googlemaps.html
  1. Google Maps V3 - 如何计算给定边界的缩放级别
  2. http://www.physicsforums.com/showthread.php?t=455491

事实上,我并不关心瓦片,我用Java进行这个项目,所以我只有一张图片,我知道它的尺寸(480像素x 480像素),我也知道它的中心坐标(纬度=47,经度=1.5,因此中心像素为240x240),以及它的缩放级别(5)。 我所需要的就是获取任何像素坐标的公式...提前感谢您。 - user1374021

1
基于Chris Broadfoot上面的数学答案和Stack Overflow上关于Mercator投影的其他代码,我得到了这个。
public class MercatorProjection implements Projection {

    private static final double DEFAULT_PROJECTION_WIDTH = 256;
    private static final double DEFAULT_PROJECTION_HEIGHT = 256;

    private double centerLatitude;
    private double centerLongitude;
    private int areaWidthPx;
    private int areaHeightPx;
    // the scale that we would need for the a projection to fit the given area into a world view (1 = global, expect it to be > 1)
    private double areaScale;

    private double projectionWidth;
    private double projectionHeight;
    private double pixelsPerLonDegree;
    private double pixelsPerLonRadian;

    private double projectionCenterPx;
    private double projectionCenterPy;

    public MercatorProjection(
            double centerLatitude,
            double centerLongitude,
            int areaWidthPx,
            int areaHeightPx,
            double areaScale
    ) {
        this.centerLatitude = centerLatitude;
        this.centerLongitude = centerLongitude;
        this.areaWidthPx = areaWidthPx;
        this.areaHeightPx = areaHeightPx;
        this.areaScale = areaScale;

        // TODO stretch the projection to match to deformity at the center lat/lon?
        this.projectionWidth = DEFAULT_PROJECTION_WIDTH;
        this.projectionHeight = DEFAULT_PROJECTION_HEIGHT;
        this.pixelsPerLonDegree = this.projectionWidth / 360;
        this.pixelsPerLonRadian = this.projectionWidth / (2 * Math.PI);

        Point centerPoint = projectLocation(this.centerLatitude, this.centerLongitude);
        this.projectionCenterPx = centerPoint.x * this.areaScale;
        this.projectionCenterPy = centerPoint.y * this.areaScale;
    }

    @Override
    public Location getLocation(int px, int py) {
        double x = this.projectionCenterPx + (px - this.areaWidthPx / 2);
        double y = this.projectionCenterPy + (py - this.areaHeightPx / 2);

        return projectPx(x / this.areaScale, y / this.areaScale);
    }

    @Override
    public Point getPoint(double latitude, double longitude) {
        Point point = projectLocation(latitude, longitude);

        double x = (point.x * this.areaScale - this.projectionCenterPx) + this.areaWidthPx / 2;
        double y = (point.y * this.areaScale - this.projectionCenterPy) + this.areaHeightPx / 2;

        return new Point(x, y);
    }

    // from https://dev59.com/A2cs5IYBdhLWcg3w433H

    Location projectPx(double px, double py) {
        final double longitude = (px - this.projectionWidth/2) / this.pixelsPerLonDegree;
        final double latitudeRadians = (py - this.projectionHeight/2) / -this.pixelsPerLonRadian;
        final double latitude = rad2deg(2 * Math.atan(Math.exp(latitudeRadians)) - Math.PI / 2);
        return new Location() {
            @Override
            public double getLatitude() {
                return latitude;
            }

            @Override
            public double getLongitude() {
                return longitude;
            }
        };
    }

    Point projectLocation(double latitude, double longitude) {
        double px = this.projectionWidth / 2 + longitude * this.pixelsPerLonDegree;
        double siny = Math.sin(deg2rad(latitude));
        double py = this.projectionHeight / 2 + 0.5 * Math.log((1 + siny) / (1 - siny) ) * -this.pixelsPerLonRadian;
        Point result = new org.opencv.core.Point(px, py);
        return result;
    }

    private double rad2deg(double rad) {
        return (rad * 180) / Math.PI;
    }

    private double deg2rad(double deg) {
        return (deg * Math.PI) / 180;
    }
}

这是一个针对原始答案的单元测试。
public class MercatorProjectionTest {

    @Test
    public void testExample() {

        // tests against values in https://dev59.com/cmPVa4cB1Zd3GeqP5nGz

        double centerLatitude = 47;
        double centerLongitude = 1.5;

        int areaWidth = 480;
        int areaHeight = 480;

        // google (static) maps zoom level
        int zoom = 5;

        MercatorProjection projection = new MercatorProjection(
                centerLatitude,
                centerLongitude,
                areaWidth,
                areaHeight,
                Math.pow(2, zoom)
        );

        Point centerPoint = projection.projectLocation(centerLatitude, centerLongitude);
        Assert.assertEquals(129.06666666666666, centerPoint.x, 0.001);
        Assert.assertEquals(90.04191318303863, centerPoint.y, 0.001);

        Location topLeftByProjection = projection.projectPx(127.1875, 82.53125);
        Assert.assertEquals(53.72271667491848, topLeftByProjection.getLatitude(), 0.001);
        Assert.assertEquals(-1.142578125, topLeftByProjection.getLongitude(), 0.001);

        // NOTE sample has some pretty serious rounding errors
        Location topLeftByPixel = projection.getLocation(0, 0);
        Assert.assertEquals(53.72271667491848, topLeftByPixel.getLatitude(), 0.05);
        // the math for this is wrong in the sample (see comments)
        Assert.assertEquals(-9, topLeftByPixel.getLongitude(), 0.05);

        Point reverseTopLeftBase = projection.projectLocation(topLeftByPixel.getLatitude(), topLeftByPixel.getLongitude());
        Assert.assertEquals(121.5625, reverseTopLeftBase.x, 0.1);
        Assert.assertEquals(82.53125, reverseTopLeftBase.y, 0.1);

        Point reverseTopLeft = projection.getPoint(topLeftByPixel.getLatitude(), topLeftByPixel.getLongitude());
        Assert.assertEquals(0, reverseTopLeft.x, 0.001);
        Assert.assertEquals(0, reverseTopLeft.y, 0.001);

        Location bottomRightLocation = projection.getLocation(areaWidth, areaHeight);
        Point bottomRight = projection.getPoint(bottomRightLocation.getLatitude(), bottomRightLocation.getLongitude());
        Assert.assertEquals(areaWidth, bottomRight.x, 0.001);
        Assert.assertEquals(areaHeight, bottomRight.y, 0.001);
    }

}

如果你正在处理航空摄影等相关工作,我觉得这个算法没有考虑到墨卡托投影的拉伸效应,所以如果你感兴趣的区域不是相对靠近赤道的话,它可能会失去精度。我想你可以通过将x坐标乘以中心点的纬度cos值来近似计算。

0
值得一提的是,实际上您可以让谷歌地图API从像素坐标中获取纬度和经度坐标。
虽然在V3版本中有点复杂,以下是如何操作的示例。
(注意: 这里假设您已经拥有要转换为纬度和经度坐标的地图和像素顶点):
let overlay  = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.onAdd = function() {};
overlay.onRemove = function() {};
overlay.setMap(map);

let latlngObj = overlay.fromContainerPixelToLatLng(new google.maps.Point(pixelVertex.x, pixelVertex.y);

overlay.setMap(null); //removes the overlay

希望能对某人有所帮助。

更新:我意识到我用了两种方法,但仍然使用相同的方式创建覆盖层(因此我不会重复那段代码)。

let point = new google.maps.Point(628.4160703464878, 244.02779437950872);
console.log(point);
let overlayProj = overlay.getProjection();
console.log(overlayProj);
let latLngVar = overlayProj.fromContainerPixelToLatLng(point);
console.log('the latitude is: '+latLngVar.lat()+' the longitude is: '+latLngVar.lng());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接