views:

2874

answers:

7

I have a city "map" (for example - Moscow). She in accuracy repeats the contours the given city in google maps (that is it is copied from google maps and it is a little processed, but the sense remained the same).

alt text

Also I have object co-ordinates in a city (in geographic coordinates).

Problem: how to convert geographic coordinates to the co-ordinates of my picture (that is in pixels on OX and OY on a picture). That is I receive geographic coordinates and it is necessary for me to draw this point on my picture.

The most desired variant of the answer - is based on javascript, but it is possible and on php.


I know that on small scales (for example on city scales) it to make simply enough (it is necessary to learn what geographic coordinates has one of picture corners, then to learn "price" of one pixel in geographic coordinates on a picture on axes OX and OY separately).

But on the big scales (country scale) "price" of one pixel will be not a constant, and will vary strongly enough and the method described above cannot be applied.

How to solve a problem on country scales?


Update:

I do not use API Google Maps, I have only: geographic coordinates of the object (they are from google maps), I still have at my site a simple picture *. gif, in which I must draw a point corresponding geographic coordinates.

+1  A: 

If each pixel is assumed to be of the same area then the following article about converting distances to longitude/latitude co-ordinates may be of some help to you:

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

zaf
It is far enough from my problem, I do not know as it it is possible to use.
Kalinin
+2  A: 

You need formulas to convert latitude and longitude to rectangular coordinates. There are a great number to choose from and each will distort the map in a different way. Wolfram MathWorld has a good collection:

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

Follow the "See Also" links.

Amos Newcombe
+2  A: 

You may have a look at code that used on gheat, it's ported from js to python.

ahmet
+2  A: 

The translation you are addressing has to do with Map Projection, which is how the spherical surface of our world is translated into a 2 dimensional rendering. There are multiple ways (projections) to render the world on a 2-D surface.

If your maps are using just a specific projection (Mercator being popular), you should be able to find the equations, some sample code, and/or some library (e.g. one Mercator solution - http://stackoverflow.com/questions/1019997/in-javascript-convert-lat-longs-to-x-y-co-ordinates. If that doesn't do it, I'm sure you can find other samples - http://stackoverflow.com/search?q=mercator. If your images aren't map(s) using a Mercator projection, you'll need to determine what projection it does use to find the right translation equations.

If you are trying to support multiple map projections (you want to support many different maps that use different projections), then you definitely want to use a library like PROJ.4, but again I'm not sure what you'll find for Javascript or PHP.

Bert F
+1 for mentioning PROJ.4.
Daniel Pryden
PROJ.4 is great, but when you're already dealing with maps you're getting from Google Maps, it's easier just to use the coordinate transformation API it already provides. This handles projection translation (Google Maps seems to use Mercatorhttp://code.google.com/apis/maps/documentation/reference.html#GMercatorProjection) and makes it easy to go from lat/long to a pixel.
Alan Donnelly
@Donnelly - Excellent point - she did say her map image matches Google's map image. GMercatorProjection should be an answer in itself - you should add it as a separate answer [alternatively, I'll update this answer to include it]. I did read the second part of her question (the part below the line) to be a more general about how to match lat/lon coordinates to map images in general (not necessarily Google), so I tried to give a more general answer. I might have been reading a much into her question.
Bert F
+4  A: 

The key to all of this is understanding map projections. As others have pointed out, the cause of the distortion is the fact that the spherical (or more accurately ellipsoidal) earth is projected onto a plane.

In order to achieve your goal, you first must know to things about your data:

  1. The projection your maps are in. If they are purely derived from Google Maps, then chances are they are using a spherical Mercator projection.
  2. The geographic coordinate system your latitude/longitude coordinates are using. This can vary, because there are different ways of locating lat/longs on the globe. The most common GCS, used in most web-mapping applications and for GPS's, is WGS84.

I'm assuming your data is in these coordinate systems.

The spherical Mercator projection defines a coordinate pair in meters, for the surface of the earth. This means, for every lat/long coordinate there is a matching meter/meter coordinate. This enables you to do the conversion using the following procedure:

  1. Find the WGS84 lat/long of the corners of the image.
  2. Convert the WGS lat/longs to the spherical Mercator projection. There conversion tools out there, my favorite is to use the cs2cs tool that is part of the PROJ4 project.
  3. You can safely do a simple linear transform to convert between points on the image, and points on the earth in the spherical Mercator projection, and back again.

In order to go from a WGS84 point to a pixel on the image, the procedure is now:

  1. Project lat/lon to spherical Mercator. This can be done using the proj4js library.
  2. Transform spherical Mercator coordinate into image pixel coordinate using the linear relationship discovered above.

You can use the proj4js library like this:

// 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
fmark
@fmark, plugged the code, as you said, the script gives an error on the line `Proj4js.Proj = OpenLayers.Class({...})` it said: `OpenLayers is not defined`. How solve the problem?
Kalinin
+2  A: 

So you want to take latitude/longitude coordinates and find out the pixel coordinates on your image of that location?

The main GMap2 class provides transformation to/from a pixel on the displayed map and a lat/long coordinate:

Gmap2.fromLatLngToContainerPixel(latlng)

For example:

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

So assuming that your map image is a screen grab of the Google Map display, then this will give you the correct pixel coordinate on that image of a lat/long coordinate.

Things are trickier if you're grabbing tile images and stitching them together yourself since the area of the complete tile set will lie outside the area of the displayed map.

In this case, you'll need to use the left and top values of the top-left image tile as an offset from the coordinates that fromLatLngToContainerPixel(latlng:GLatLng) gives you, subtracting the left coordinate from the x coordinate and top from the y coordinate. So if the top-left image is positioned at (-50, -122) (left, top), and fromLatLngToContainerPixel() tells you a lat/long is at pixel coordinate (150, 320), then on the image stitched together from tiles, the true position of the coordinate is at (150 - (-50), 320 - (-122)) which is (200, 442).

It's also possible that a similar GMap2 coordinate translation function:

GMap2.fromLatLngToDivPixel(latlng:GLatLng)

will give you the correct lat/long to pixel translation for the stitched-tiles case - I've not tested this, nor is it 100% clear from the API docs.

See here for more: http://code.google.com/apis/maps/documentation/reference.html#GMap2.Methods.Coordinate-Transformations

Alan Donnelly
A: 

You will have to implement the Google Maps API projection in your language. I have the C# source code for this:

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

Source:

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

Jader Dias