Преобразование длинных/латов в пиксель x/y на заданный снимок

У меня есть карта города Москвы. Мы изменили изображение Google Maps с некоторыми элементами искусства, но связь между координатами GPS и пикселями осталась прежней.

Проблема: Как преобразовать координаты GPS из разных точек данных, которые мы имеем в пиксельные координаты на изображении?

В идеале я могу сделать это в Javascript, но PHP будет в порядке.


Я знаю, что на небольших масштабах (например, на городских масштабах) это достаточно просто (необходимо узнать, какие географические координаты имеют один из углов изображения, а затем узнать "цену" одного пикселя в географических координатах на изображение по осям OX и OY отдельно).

Но в больших масштабах (страновой шкале) "цена" одного пикселя будет не постоянной и будет сильно различаться, и описанный выше метод не может быть применен.

Как решить проблему на страновых шкалах?


Update:

Я не использую API Google Maps, у меня есть только: географические координаты объекта (они из карт Google), у меня все еще есть на моем сайте простая фотография *. gif, в котором я должен нарисовать точку, соответствующую географическим координатам.

Ответ 1

Ключом ко всему этому является понимание картографических прогнозов. Как указывали другие, причиной искажения является тот факт, что сферическая (или более точно эллипсоидальная) земля проецируется на плоскость.

Чтобы достичь своей цели, вы сначала должны знать две вещи о своих данных:

  • Проецирование ваших карт. Если они получены исключительно из Карт Google, то, скорее всего, они используют сферическая проекция Меркатора.
  • Используется географическая система координат, которую используют ваши координаты широты/долготы. Это может различаться, поскольку существуют различные способы нахождения lat/longs на земном шаре. Наиболее распространенные GCS, используемые в большинстве приложений для веб-картографии и для GPS, WGS84.

Я предполагаю, что ваши данные находятся в этих системах координат.

Сферическая проекция Меркатора определяет координатную пару в метрах для поверхности земли. Это означает, что для каждой координаты lat/long имеется соответствующая координата метра/метра. Это позволяет выполнить преобразование, используя следующую процедуру:

  • Найдите WGS84 лат/длинний углов изображения.
  • Преобразование WGS lat/longs в сферическую проекцию Меркатора. Там инструменты конвертации, мой любимый - использовать инструмент cs2cs, который является частью проекта PROJ4.
  • Вы можете смело сделать простой линейный перевод для преобразования между точками изображения и точками на земле в сферической проекции Меркатора и обратно.

Чтобы перейти от точки WGS84 к пикселю на изображении, процедура теперь:

  • Проект lat/lon для сферического Меркатора. Это можно сделать, используя библиотеку proj4js.
  • Преобразование сферической координаты Меркатора в координату пикселя изображения с использованием найденной выше линейной связи.

Вы можете использовать библиотеку proj4js следующим образом:

// include the library
<script src="lib/proj4js-combined.js"></script>  //adjust the path for your server
                                                 //or else use the compressed version
// creating source and destination Proj4js objects
// once initialized, these may be re-used as often as needed
var source = new Proj4js.Proj('EPSG:4326');    //source coordinates will be in Longitude/Latitude, WGS84
var dest = new Proj4js.Proj('EPSG:3785');     //destination coordinates in meters, global spherical mercators projection, see http://spatialreference.org/ref/epsg/3785/


// transforming point coordinates
var p = new Proj4js.Point(-76.0,45.0);   //any object will do as long as it has 'x' and 'y' properties
Proj4js.transform(source, dest, p);      //do the transformation.  x and y are modified in place

//p.x and p.y are now EPSG:3785 in meters

Ответ 2

Вам нужно будет реализовать проекцию API Карт Google на своем языке. У меня есть исходный код С# для этого:

public class GoogleMapsAPIProjection
{
    private readonly double PixelTileSize = 256d;
    private readonly double DegreesToRadiansRatio = 180d / Math.PI;
    private readonly double RadiansToDegreesRatio = Math.PI / 180d;
    private readonly PointF PixelGlobeCenter;
    private readonly double XPixelsToDegreesRatio;
    private readonly double YPixelsToRadiansRatio;

    public GoogleMapsAPIProjection(double zoomLevel)
    {
        var pixelGlobeSize = this.PixelTileSize * Math.Pow(2d, zoomLevel);
        this.XPixelsToDegreesRatio = pixelGlobeSize / 360d;
        this.YPixelsToRadiansRatio = pixelGlobeSize / (2d * Math.PI);
        var halfPixelGlobeSize = Convert.ToSingle(pixelGlobeSize / 2d);
        this.PixelGlobeCenter = new PointF(
            halfPixelGlobeSize, halfPixelGlobeSize);
    }

    public PointF FromCoordinatesToPixel(PointF coordinates)
    {
        var x = Math.Round(this.PixelGlobeCenter.X
            + (coordinates.X * this.XPixelsToDegreesRatio));
        var f = Math.Min(
            Math.Max(
                 Math.Sin(coordinates.Y * RadiansToDegreesRatio),
                -0.9999d),
            0.9999d);
        var y = Math.Round(this.PixelGlobeCenter.Y + .5d * 
            Math.Log((1d + f) / (1d - f)) * -this.YPixelsToRadiansRatio);
        return new PointF(Convert.ToSingle(x), Convert.ToSingle(y));
    }

    public PointF FromPixelToCoordinates(PointF pixel)
    {
        var longitude = (pixel.X - this.PixelGlobeCenter.X) /
            this.XPixelsToDegreesRatio;
        var latitude = (2 * Math.Atan(Math.Exp(
            (pixel.Y - this.PixelGlobeCenter.Y) / -this.YPixelsToRadiansRatio))
            - Math.PI / 2) * DegreesToRadiansRatio;
        return new PointF(
            Convert.ToSingle(latitude),
            Convert.ToSingle(longitude));
    }
}

Источник:

http://code.google.com/p/geographical-dot-net/source/browse/trunk/GeographicalDotNet/GeographicalDotNet/Projection/GoogleMapsAPIProjection.cs

Ответ 3

Итак, вы хотите получить координаты широты/долготы и узнать координаты пикселей на вашем изображении этого местоположения?

Основной класс GMap2 обеспечивает преобразование в/из пикселя на отображаемой карте и координату lat/long:

Gmap2.fromLatLngToContainerPixel(latlng)

Например:

var gmap2 = new GMap2(document.getElementById("map_canvas"));
var geocoder = new GClientGeocoder();

geocoder.getLatLng( "1600 Pennsylvania Avenue NW Washington, D.C. 20500",
    function( latlng ) {
        var pixel_coords = gmap2.fromLatLngToContainerPixel(latlng);

        window.alert( "The White House is at pixel coordinates (" + 
            pixel_coodrs.x + ", " + pixel_coords.y + ") on the " +
            "map image shown on this page." );
    }
);

Итак, предполагая, что ваше изображение карты является захватом экрана на экране Google Map, тогда это даст вам правильную координату пикселя на этом изображении координаты lat/long.

Все сложнее, если вы захватываете изображения плитки и сшиваете их вместе, так как область полного набора плитки будет находиться за пределами области отображаемой карты.

В этом случае вам нужно будет использовать левое и верхнее значения верхней части изображения в качестве смещения от координат, которые дает fromLatLngToContainerPixel (latlng: GLatLng), вычитая левую координату из координаты x и сверху от координаты y. Поэтому, если верхнее левое изображение расположено на (-50, -122) (слева, сверху), а fromLatLngToContainerPixel() указывает, что lat/long имеет координату пикселя (150, 320), затем на изображении, сшитом вместе (150 - (-50), 320 - (-122)), который равен (200, 442).

Также возможно, что аналогичная функция преобразования координат GMap2:

GMap2.fromLatLngToDivPixel(latlng:GLatLng)

предоставит вам правильный перевод lat/long to pixel для случая сшитой черепицы - я не тестировал это и не на 100% удаляюсь из документов API.

Смотрите здесь: http://code.google.com/apis/maps/documentation/reference.html#GMap2.Methods.Coordinate-Transformations

Ответ 5

Перевод, к которому вы обращаетесь, относится к Map Projection, в результате чего сферическая поверхность нашего мира преобразуется в двумерную рендеринг. Существует несколько способов (проекций), чтобы отобразить мир на двумерной поверхности.

Если ваши карты используют только определенную проекцию (Mercator, популярность), вы должны иметь возможность находить уравнения, некоторые образец кода и/или некоторую библиотеку (например, одно решение Меркатора - Преобразование Lat/Longs в координаты X/Y Если это не так, m Конечно, вы можете найти другие образцы - https://stackoverflow.com/search?q=mercator. Если ваши изображения не являются картами с использованием проекции Меркатора, вам необходимо определить, какая проекция используется для нахождения правильных уравнений переноса.

Если вы пытаетесь поддерживать несколько проекций карт (вы хотите поддерживать множество разных карт, которые используют разные проекции), то вы определенно хотите использовать библиотеку, например PROJ.4, но опять же я не уверен, что вы найдете для Javascript или PHP.

Ответ 6

Вам нужны формулы для преобразования широты и долготы в прямоугольные координаты. Есть огромное количество на выбор, и каждый из них будет искажать карту по-другому. Wolfram MathWorld имеет хорошую коллекцию:

http://mathworld.wolfram.com/MapProjection.html

Следуйте ссылкам "См. также".

Ответ 8

Одной из важных вещей, которые необходимо учитывать, является "масштабирование" уровня вашей проекции (в частности, для Карт Google).

Как объясняет Google:

При уровне масштабирования 1 карта состоит из 4 256x256 пикселей, в результате чего в пиксельном пространстве от 512x512. На уровне масштабирования 19 каждый пиксель x и y на на карту можно ссылаться, используя значение от 0 до 256 * 2 ^ 19

(см. https://developers.google.com/maps/documentation/javascript/maptypes?hl=en#MapCoordinates)

Чтобы повлиять на значение "масштабирования", я рекомендую простые и эффективные функции deltaLonPerDeltaX и deltaLatPerDeltaY ниже. Хотя x-пиксели и долготы строго пропорциональны, это не относится к y-пикселям и широтам, для которых формула требует начальной широты.

// Adapted from : http://blog.cppse.nl/x-y-to-lat-lon-for-google-maps


window.geo = {

    glOffset: Math.pow(2,28), //268435456,
    glRadius:  Math.pow(2,28) / Math.PI,
    a: Math.pow(2,28),
    b: 85445659.4471,
    c: 0.017453292519943,
    d: 0.0000006705522537,
    e: Math.E, //2.7182818284590452353602875,
    p: Math.PI / 180,

    lonToX: function(lon) {
        return Math.round(this.glOffset + this.glRadius * lon * this.p);
    },

    XtoLon: function(x) {
        return -180 + this.d * x;
    },

    latToY: function(lat) {
        return Math.round(this.glOffset - this.glRadius *
                          Math.log((1 + Math.sin(lat * this.p)) /
                          (1 - Math.sin(lat * this.p))) / 2);
    },

    YtoLat: function(y) {
        return Math.asin(Math.pow(this.e,(2*this.a/this.b - 2*y/this.b)) /
                                 (Math.pow(this.e, (2*this.a/this.b - 2*y/this.b))+1) -
                                 1/(Math.pow(this.e, (2*this.a/this.b - 2*y/this.b))+1)
                        ) / this.c;
    },

    deltaLonPerDeltaX: function(deltaX, zoom) {
        // 2^(7+zoom) pixels <---> 180 degrees
        return deltaX * 180 / Math.pow(2, 7+zoom);
    },

    deltaLatPerDeltaY: function(deltaY, zoom, startLat) {
        // more complex because of the curvature, we calculte it by difference
        var startY = this.latToY(startLat),
            endY = startY + deltaY * Math.pow(2, 28-7-zoom),
            endLat = this.YtoLat(endY);

        return ( endLat - startLat ); // = deltaLat
    }
}

Ответ 9

мой подход работает без библиотеки и с обрезанными картами. Это означает, что он работает только с частями изображения Mercator. Возможно, это помогает кому-то: fooobar.com/questions/95279/...

Ответ 10

Борьба с этим - иметь как открытую карту, так и уличную карту google и хотел спроецировать внешний графический образ

var map = new OpenLayers.Map({
            div:"map-id",
            allOverlays: true
    });
    var osm = new OpenLayers.Layer.OSM("OpenStreeMao");
    var gmap = new OpenLayers.Layer.Google("Google Streets", {visibility: false});

    map.addLayers([osm,gmap]);

    var vectorLayer = new OpenLayers.Layer.Vector("IconLayer");


    var lonlatObject = new OpenLayers.LonLat(24.938622,60.170421).transform(
            new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject()
    );
    console.log(lonlatObject);

    var point = new OpenLayers.Geometry.Point(lonlatObject.lon, lonlatObject.lat);
    console.log(point);

    var point2 = new OpenLayers.Geometry.Point(lonlatObject.x, lonlatObject.y);
    console.log(point2);

    var feature = new OpenLayers.Feature.Vector(point, null, {
        externalGraphic:  "http://cdn1.iconfinder.com/data/icons/SUPERVISTA/networking/png/72/antenna.png",
        graphicWidth: 72,
        graphicHeight: 72,
        fillOpacity: 1
    });


    vectorLayer.addFeatures(feature);

    map.addLayer(vectorLayer);


    map.setCenter(
            new OpenLayers.LonLat(24.938622,60.170421).transform(
            new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject()
            ),
            12);

     map.addControl(new OpenLayers.Control.LayerSwitcher());

http://jsfiddle.net/alexcpn/N9dLN/8/