Конвертировать long / lat в pixel x / y на данной картинке - PullRequest
62 голосов
/ 16 апреля 2010

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

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

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


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

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

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


Обновление:

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

Ответы [ 10 ]

67 голосов
/ 22 апреля 2010

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

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

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

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

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

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

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

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

Вы можете использовать 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
16 голосов
/ 25 апреля 2010

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

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

8 голосов
/ 22 апреля 2010

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

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

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, то это даст вам правильную координату пикселя для этого изображения с широтой / долготой.

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

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

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

GMap2.fromLatLngToDivPixel(latlng:GLatLng)

даст вам правильное преобразование широты / длины в пиксель для случая сшитых плиток - я не проверял это, и при этом он не на 100% ясен из документации API.

Смотрите здесь для получения дополнительной информации: http://code.google.com/apis/maps/documentation/reference.html#GMap2.Methods.Coordinate-Transformations

7 голосов
/ 17 апреля 2010

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

5 голосов
/ 22 апреля 2010

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

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

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

3 голосов
/ 08 августа 2014

Одной из важных вещей, которые необходимо учитывать, является уровень масштабирования вашей проекции (в частности, для Карт 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
    }
}
3 голосов
/ 16 апреля 2010

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

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

Перейдите по ссылкам «Смотрите также».

3 голосов
/ 16 апреля 2010

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

http://www.johndcook.com/blog/2009/04/27/converting-miles-to-degrees-longitude-or-latitude/

2 голосов
/ 02 мая 2012

мой подход работает без библиотеки и с обрезанными картами. Означает, что это работает только с частями из изображения Mercator. Может быть, это кому-нибудь поможет: https://stackoverflow.com/a/10401734/730823

0 голосов
/ 22 января 2013

Боролись с этим - имейте и открытую карту, и карту улиц 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/

...