Получение долготы / широты из координат пикселей в статической карте Google

У меня есть проект JAVA, который нужно реализовать с помощью Google Static Maps, и после нескольких часов работы я не могу ничего заработать, я все объясню и надеюсь, что кто-то сможет мне помочь.

Я использую статическую карту (480 x 480 пикселей), центр карты - широта = 47, долгота = 1,5, а уровень масштабирования - 5.

Теперь мне нужно иметь возможность получать широту и долготу, когда я щелкаю пиксель на этой статической карте. После некоторых поисков я обнаружил, что мне следует использовать проекцию Меркатора (верно?), Я также обнаружил, что каждый уровень масштабирования удваивает точность как по горизонтали, так и по вертикали, но я не могу найти правильную формулу для связывания пикселей, уровня масштабирования и широты. / долг ...

Моя проблема заключается только в том, чтобы получить широту / долготу из пикселя, зная координаты центра и пиксель, а также уровень масштабирования ...

Заранее спасибо !


person user1374021    schedule 04.05.2012    source источник
comment
У вас есть ссылка на страницу, где вы узнали о проекции Меркатора?   -  person Sean Mickey    schedule 04.05.2012


Ответы (4)


Используйте проекцию Меркатора.

Если вы проецируете в пространство [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)
person Chris Broadfoot    schedule 04.05.2012
comment
Спасибо за ответ, но я думаю, что что-то не так. Вы действительно говорите, что мой верхний левый угол находится в LatLng (53.72271667491848, -1.142578125), что в Великобритании, но на самом деле левый верхний угол моей карты больше в Ирландии, посмотрите: i50.tinypic.com/9vb2b7.jpg Вы можете увидеть мое окно, - карта центрирована по LatLng (47,1,5) - размер карты 480x480 - уровень масштабирования карты 5 - ›Полагаю, LatLng (47,1.5) == Point (240,240) нет? (кажется правдой, когда я наводил указатель мыши на точку (240,240), я действительно близок к LatLng (47,1,5)) Заранее спасибо - person user1374021; 06.05.2012
comment
@ user1374021: старая ветка, я знаю, но я работаю над этой проблемой, и этот ответ мне очень помог. проблема была в математике для вычисления x; это должно быть x = 4130 - 480/2 = 3890. y = 2641 правильный. когда вы вводите эти числа, вы получите значение -9,00, которое соответствует вашему изображению. - person 4mla1fn; 01.01.2017

Google-maps использует тайлы для карты, чтобы эффективно разделить мир на сетку размером 256 ^ 21 пиксель. В основном мир состоит из 4 плиток при самом низком увеличении. Когда вы начинаете увеличивать масштаб, вы получаете 16 плиток, затем 64 плитки, а затем 256 плиток. Это в основном квадродерево. Поскольку такая 1d-структура может сглаживать только 2d, вам также понадобится проекция меркантора или преобразование в WGS 84. Вот хороший ресурс Преобразование долготы / широты в пиксели x / y на заданном изображении. В Google Maps есть функция преобразования пары долгота-долгота в пиксель. Вот ссылка, но в ней указано, что плитки имеют размер только 128x128: http://michal.guerquin.com/googlemaps.html.

  1. Карты Google V3 - Как использовать рассчитать уровень масштабирования для заданных границ
  2. http://www.physicsforums.com/showthread.php?t=455491
person Gigamegs    schedule 04.05.2012
comment
На самом деле меня не волнуют плитки, я делаю этот проект на Java, поэтому все, что у меня есть, это изображение, размер которого я знаю (480 пикселей x 480 пикселей), и я знаю координаты его центра (lat = 47, lon = 1.5, поэтому центральный пиксель равен 240x240) и его уровень масштабирования (5). Все, что мне нужно, это формула для получения любых пиксельных координат ... Заранее спасибо - person user1374021; 04.05.2012

На основе математики в ответе Криса Бродфута выше и некоторого другого кода на Переполнение стека для проекции Меркатора, я получил это

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 (широту) центра?

person user1691694    schedule 07.01.2017

Похоже, стоит упомянуть, что API карт Google действительно может выдавать координаты широты и долготы на основе координат пикселей.

Хотя в 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());
person MER    schedule 17.08.2017