web-dev-qa-db-fra.com

Convertir long/lat en pixel x/y sur une image donnée

J'ai une carte de la ville de Moscou. Nous avons modifié une image Google Maps avec certains éléments artistiques, mais la relation entre les coordonnées GPS et les pixels reste la même.

Problème: Comment convertir les coordonnées GPS de divers points de données que nous avons en coordonnées de pixel dans l’image?

Idéalement, je peux le faire en Javascript, mais PHP serait OK.


Je sais que sur de petites échelles (par exemple sur des échelles de ville) il suffit de savoir simplement (il est nécessaire de savoir quelles coordonnées géographiques ont l'un des coins de l'image, puis d'apprendre le "prix" d'un pixel en coordonnées géographiques sur une image sur des axes OX et OY séparément).

Mais sur les grandes échelles (échelle du pays), le "prix" d'un pixel ne sera pas une constante, il variera suffisamment et la méthode décrite ci-dessus ne pourra pas être appliquée.

Comment résoudre un problème à l'échelle d'un pays?


Mettre à jour:

Je n'utilise pas l'API Google Maps, je n'ai que: les coordonnées géographiques de l'objet (elles proviennent de google maps), j'ai toujours sur mon site une image simple *. gif, dans lequel je dois dessiner un point correspondant aux coordonnées géographiques.

61
Kalinin

La clé de tout cela est de comprendre les projections cartographiques . Comme d'autres l'ont fait remarquer, la distorsion est due au fait que la terre sphérique (ou plus précisément ellipsoïdale) est projetée sur un plan. 

Pour atteindre votre objectif, vous devez d’abord connaître deux choses à propos de vos données:

  1. La projection dans laquelle se trouvent vos cartes. Si elles proviennent uniquement de Google Maps , elles utilisent probablement une projection sphérique Mercator
  2. Le système de coordonnées géographiques que vos coordonnées de latitude/longitude utilisent. Cela peut varier, car il existe différentes manières de localiser les lat/long sur le globe. Le système GCS le plus courant, utilisé dans la plupart des applications de cartographie Web et pour les GPS, est WGS84 .

Je suppose que vos données sont dans ces systèmes de coordonnées.

La projection sphérique de Mercator définit une paire de coordonnées en mètres pour la surface de la Terre. Cela signifie que pour chaque coordonnée lat/long, il existe une coordonnée mètre/mètre correspondante. Cela vous permet d'effectuer la conversion en utilisant la procédure suivante:

  1. Trouvez le WGS84 lat/long des coins de l’image.
  2. Convertissez le WGS lat/long en projection sphérique de Mercator. Il existe des outils de conversion. Mon préféré est d’utiliser l’outil cs2cs qui fait partie du projet PROJ4
  3. Vous pouvez effectuer en toute sécurité une simple transformation linéaire pour convertir entre des points de l'image et des points de la terre dans la projection sphérique de Mercator, et inversement.

Pour passer d'un point WGS84 à un pixel de l'image, la procédure est la suivante: 

  1. Projet lat/lon sur Mercator sphérique. Cela peut être fait en utilisant la bibliothèque proj4js.
  2. Transformez la coordonnée Mercator sphérique en coordonnée pixel de l’image en utilisant la relation linéaire découverte ci-dessus.

Vous pouvez utiliser la bibliothèque proj4js comme ceci:

// 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
66
fmark

Vous devrez implémenter la projection de Google Maps API dans votre langue. J'ai le code source C # pour cela: 

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));
    }
}

La source:

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

16
Jader Dias

Donc, vous voulez prendre les coordonnées de latitude/longitude et trouver les coordonnées de pixel sur votre image de cet emplacement?

La classe principale GMap2 fournit la transformation vers/à partir d'un pixel sur la carte affichée et une coordonnée lat/long:

Gmap2.fromLatLngToContainerPixel(latlng)

Par exemple:

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." );
    }
);

Donc, en supposant que votre image de carte soit une capture d'écran de l'affichage de Google Map, cela vous donnera la coordonnée de pixel correcte sur cette image d'une coordonnée lat/long.

Les choses sont plus compliquées si vous prenez des images de mosaïque et que vous les assemblez vous-même, car la totalité de la mosaïque se trouvera en dehors de la carte affichée.

Dans ce cas, vous devez utiliser les valeurs gauche et supérieure de la mosaïque d'image supérieure gauche comme décalage par rapport aux coordonnées fournies par fromLatLngToContainerPixel (latlng: GLatLng), en soustrayant la coordonnée gauche de la coordonnée x et en haut de la y coordonne. Donc, si l’image en haut à gauche est positionnée à (-50, -122) (à gauche, en haut), et fromLatLngToContainerPixel () vous indique qu’un lat/long est à la coordonnée en pixel (150, 320), puis sur l’image assemblée à partir de tuiles, la vraie position de la coordonnée est à (150 - (-50), 320 - (-122)) qui est (200, 442).

Il est également possible qu’une fonction de traduction de coordonnées GMap2 similaire:

GMap2.fromLatLngToDivPixel(latlng:GLatLng)

vous donnera la traduction lat/long en pixels correcte pour le cas de stitched-tiles - je ne l'ai pas testée, et les documents de l'API ne l'excluent pas à 100%.

Voir ici pour plus d'informations: http://code.google.com/apis/maps/documentation/reference.html#GMap2.Methods.Coordinate-Transformations

8
Alan Donnelly

Vous pouvez jeter un oeil sur le code utilisé sur gheat , il est transféré de js à python.

7
Ahmet AYGÜN

La traduction dont vous parlez concerne Projection cartographique , qui permet de traduire la surface sphérique de notre monde en un rendu bidimensionnel. Il existe plusieurs façons (projections) de rendre le monde sur une surface à deux dimensions.

Si vos cartes utilisent uniquement une projection spécifique ( Mercator étant populaire), vous devriez pouvoir trouver les équations, un exemple de code et/ou une bibliothèque (par exemple, une solution Mercator - Convertir Lat/Longs en Coordonnées X/Y . Si cela ne fonctionne pas, je suis sûr que vous pouvez trouver d'autres exemples - https://stackoverflow.com/search?q=mercator . Si vos images ne sont pas " t carte (s) utilisant une projection de Mercator, vous devez déterminer quelle projection elle utilise pour trouver les bonnes équations de traduction.

Si vous essayez de prendre en charge plusieurs projections de cartes (vous voulez prendre en charge de nombreuses cartes différentes qui utilisent différentes projections), vous voulez certainement utiliser une bibliothèque telle que PROJ.4 , mais là encore, je ne suis pas sûr de ce que vous ' Vous trouverez pour Javascript ou PHP.

5
Bert F

Vous avez besoin de formules pour convertir la latitude et la longitude en coordonnées rectangulaires. Il y a un grand nombre de choix et chacun déformera la carte d'une manière différente. Wolfram MathWorld a une bonne collection:

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

Suivez les liens "Voir aussi".

3
Amos Newcombe

Si chaque pixel est supposé appartenir à la même zone, l'article suivant sur la conversion des distances en coordonnées de longitude/latitude peut vous être utile:

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

3
zaf

Un des éléments importants à prendre en compte est le niveau de "zoom" de votre projection (pour Google Maps en particulier).

Comme l'explique Google: 

Au niveau de zoom 1, la carte est composée de 4 mosaïques de 256x256 pixels, générant dans un espace de pixel de 512x512. Au niveau de zoom 19, chaque pixel x et y est allumé la carte peut être référencée avec une valeur comprise entre 0 et 256 * 2 ^ 19

(Voir https://developers.google.com/maps/documentation/javascript/maptypes?hl=fr#MapCoordinates )

Pour prendre en compte la valeur "zoom", je vous recommande les fonctions simples et efficaces deltaLonPerDeltaX et deltaLatPerDeltaY ci-dessous. Bien que les x pixels et les longitudes soient strictement proportionnels, ce n'est pas le cas pour les pixels y et les latitudes, pour lesquels la formule requiert la latitude initiale.

// 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
Louis LC

mon approche fonctionne sans bibliothèque et avec des cartes recadrées. Cela signifie qu'il fonctionne uniquement avec des éléments d'une image Mercator. Peut-être que cela aide quelqu'un: https://stackoverflow.com/a/10401734/730823

2
Raphael

Lutte avec ceci - Avoir openstreet map et google street map et vouloir projeter une image graphique externe

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/

0
Alex Punnen