Java 从Google静态地图中的像素坐标获取lon/lat

Java 从Google静态地图中的像素坐标获取lon/lat,java,google-maps,google-maps-api-2,Java,Google Maps,Google Maps Api 2,我有一个使用谷歌静态地图的JAVA项目要做,在工作了好几个小时后,我一件事也做不到,我会解释一切,我希望有人能帮助我 我使用的是静态贴图(480像素x 480像素),贴图的中心是lat=47,lon=1.5,缩放级别是5 现在我需要的是,当我点击静态地图上的一个像素时,能够得到lat和lon。经过一些搜索,我发现我应该使用墨卡托投影(对吗?),我还发现每个缩放级别在水平和垂直维度上的精度都是原来的两倍,但我找不到正确的公式来链接像素、缩放级别和横向/纵向 我的问题只是从像素中获取lat/lon,

我有一个使用谷歌静态地图的JAVA项目要做,在工作了好几个小时后,我一件事也做不到,我会解释一切,我希望有人能帮助我

我使用的是静态贴图(480像素x 480像素),贴图的中心是lat=47,lon=1.5,缩放级别是5

现在我需要的是,当我点击静态地图上的一个像素时,能够得到lat和lon。经过一些搜索,我发现我应该使用墨卡托投影(对吗?),我还发现每个缩放级别在水平和垂直维度上的精度都是原来的两倍,但我找不到正确的公式来链接像素、缩放级别和横向/纵向

我的问题只是从像素中获取lat/lon,了解中心坐标和像素以及缩放级别


提前谢谢你

谷歌地图使用地图的分幅将世界有效地划分为256^21像素的分幅网格。基本上,世界是由4个最小缩放的瓷砖组成的。当您开始缩放时,会得到16个分幅,然后是64个分幅,然后是256个分幅。它基本上是一个四叉树。因为这样的1d结构只能展平2d,所以还需要进行mercantor投影或转换为WGS 84。这是一个很好的资源。谷歌地图中有一个功能,可以将lat-long对转换为像素。这里有一个链接,但上面说的磁贴仅为128x128:


  • 使用墨卡托投影

    如果通过
    [0256]
    将项目投影到
    [0256]
    的空间中:

    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)
    

    根据Chris Broadfoot上面答案中的数学,我得到了这个

    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://stackoverflow.com/questions/12507274/how-to-get-bounds-of-a-google-static-map
    
        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://stackoverflow.com/questions/10442066/getting-lon-lat-from-pixel-coords-in-google-static-map
    
            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(纬度)来近似中心坐标?

    值得一提的是,您实际上可以使用谷歌地图API从像素坐标中获取纬度和纵向坐标

    虽然在V3中有点复杂,但这里有一个如何实现的示例。
    (注意:这是假设您已经有了一个贴图,并且像素顶点要转换为纬度坐标(&lng)):

    希望这能帮助别人

    更新:我意识到我有两种方法来实现这一点,两种方法仍然使用相同的方法来创建覆盖(所以我不会重复代码)


    你有一个链接到你学习墨卡托投影的页面吗?事实上,我不“关心”瓷砖,我用Java做这个项目,所以我只有一张图片,我知道它的大小(480像素x 480像素),我知道它的中心坐标(lat=47,lon=1.5,所以中心的像素是240x240)和它的缩放级别(5).我所需要的只是得到任何像素坐标的公式…谢谢你的回复,但我觉得有点不对劲。你说我的左上角在LatLng(53.72271667491848,-1.142578125)这是在英国,但事实上,我的地图的左上角更多的是在爱尔兰,看一看:你可以看到我的窗口,-地图以LatLng(47,1.5)为中心-地图的大小是480x480-地图的缩放级别是5-->所以我猜LatLng(47,1.5)=点(240240)不?(当我把鼠标放在点(240240)上时,这似乎是真的)我真的很接近LatLng(47,1.5))谢谢你advance@user1374021:旧线程,我知道,但我正在解决这个问题,这个答案是一个很大的帮助。问题是计算x的数学;它应该是x=4130-480/2=3890。y=2641是正确的。当你推这些数字时,你会得到与你的图像相匹配的-9.00的a lon。
    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());