Преобразование long/lat в пиксель x / y на данном изображении


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

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

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


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

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

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


обновление:

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

10 61

10 ответов:

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

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

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

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

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

  1. найти WGS84 lat / long углов изображения.
  2. преобразование WGS lat / Long в сферическую проекцию Меркатора. Там инструменты преобразования там, мой любимый, чтобы использовать инструмент cs2cs, который является частью проект виде proj4.
  3. вы можете безопасно выполнить простое линейное преобразование для преобразования между точками на изображении и точками на земле в сферической проекции Меркатора и обратно.

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

  1. проект lat / lon для сферического Меркатора. Это можно сделать с помощью библиотека 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

вам придется реализовать проекцию Google Maps API на вашем языке. У меня есть исходный код 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

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

основной класс 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

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

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

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

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

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

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

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

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

следовать "см. Также".

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

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

боролся с этим-есть как openstreet map, так и google street map и хотел спроектировать внешнее графическое изображение

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/