Google Maps Static API - Get SW and NE by center coordinate
Asked Answered
I

1

10

I'm loading map tiles from Google Maps using Static Maps, using .NET.

The problem I'm having is that I don't know what the SW and NE coordinates are of the returned image.

I've found many different code samples, formulas, but they all seem to be flawed. This is the one that got closest to the correct answer. When I entered the coordinates in Google Maps it showed that it was a little bit off.

var result = GoogleMapsAPI.GetBounds(new Coordinate(4.79635, 51.15479), 20, 512, 512);

public static class GoogleMapsAPI
{
    public static MapCoordinates GetBounds(Coordinate center, int zoom, int mapWidth, int mapHeight)
    {
        var scale = Math.Pow(2, zoom);

        var SWPoint = new Coordinate(center.X - (mapWidth / 2) / scale, center.Y - (mapHeight / 2) / scale);
        var NEPoint = new Coordinate(center.X + (mapWidth / 2) / scale, center.Y + (mapHeight / 2) / scale);

        return new MapCoordinates() { SouthWest = SWPoint, NorthEast = NEPoint };
    }
}

public class MapCoordinates
{
    public Coordinate SouthWest { get; set; }
    public Coordinate NorthEast { get; set; }
}

public class Coordinate
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }

    public double X { get { return Latitude; } set { Latitude = value; } }
    public double Y { get { return Longitude; } set { Longitude = value; } }

    public Coordinate(double lat, double lng)
    {
        Latitude = lat;
        Longitude = lng;
    }

    public override string ToString()
    {
        return X.ToString() + ", " + Y.ToString();
    }
}

Sources:
How to get bounds of a google static map?
http://www.easywms.com/easywms/?q=zh-hans/node/3612

Loaded image by center coordinate:
http://maps.googleapis.com/maps/api/staticmap?center=51.15479,4.79635&zoom=20&size=512x512&sensor=false

Faulty SW:
https://maps.google.be/maps?q=51.154545859375,+4.796105859375&hl=en&ll=51.154763,4.796695&spn=0.000568,0.001635&sll=51.154687,4.796838&sspn=0.001136,0.00327&t=m&z=20

Faulty NE:
https://maps.google.be/maps?q=+51.155034140625,+4.796594140625&hl=en&ll=51.154764,4.796684&spn=0.000568,0.001635&sll=51.154599,4.796723&sspn=0.001136,0.00327&t=m&z=20

Indigestive answered 2/10, 2012 at 9:50 Comment(0)
N
6

EDIT: Wow, I just realized this question is almost two years old. Sorry about that.

OK so I think there are a few things going on here. The most important is that you aren't doing the Mercator projection mentioned in the links you provided. You can see it in action with a code sample in the google maps API docs. I think the reason you are getting somewhat close is that the scale factor is so large at zoom level 20 that it drowns out the details of the problem.

To get the bounds, you need to take the center lat/lon, convert it to pixel coordinates, add/subtract to get the pixel coordinates of the corners that you want, and then convert back to lat/lon. The code in the first link can do the projection and inverse projection. Below I've translated it into c#.

In your solution above, you are taking lat/lon coordinates and adding/subtracting world coordinates (pixel coords / scale), so you are combining two different coordinate systems.

An issue I ran into while trying to figure this out is that your Coordinate class is a little confusing. Hopefully I don't have this backwards, but it looks like you are putting in the latitude and longitude backwards. This is important because the Mercator projection is different in each direction, among other reasons. Your X/Y mapping seems backwards, too, because Latitude is your North/South position, which should be the Y coordinate when projected, and Longitude is you East/West position, which should be the X coordinate when projected.

To find the bounds, three coordinate systems are needed: Longitude/Latitude, World Coordinates and Pixel Coordinates. The process is Center Lat/Lon -(Mercator Projection)-> Center World Coordinates -> Center Pixel Coordinates -> NE/SW Pixel Coordinates -> NE/SW World Coordinates -(Inverse Mercator Projection)-> NE/SW Lat/Lon.

You find the pixel coordinates by adding/subtracting the dimensions of the image/2. The pixel coordinate system starts from the upper left corner though, so to get the NE corner you need to add width/2 from x and subtract height/2 from y. For the SW corner you need to subtract width/2 from x and add height/2 to y.

Here's the projection code (as part of your GoogleMapsAPI class) in c#, translation from the first link above javascript:

static GoogleMapsAPI()
{
    OriginX =  TileSize / 2;
    OriginY =  TileSize / 2;
    PixelsPerLonDegree = TileSize / 360.0;
    PixelsPerLonRadian = TileSize / (2 * Math.PI);
}

public static int TileSize = 256;
public static double OriginX, OriginY;
public static double PixelsPerLonDegree;
public static double PixelsPerLonRadian;

public static double DegreesToRadians(double deg)
{
    return deg * Math.PI / 180.0;
}

public static double RadiansToDegrees(double rads)
{
    return rads * 180.0 / Math.PI;
}

public static double Bound(double value, double min, double max)
{
    value = Math.Min(value, max);
    return Math.Max(value, min);       
}

//From Lat, Lon to World Coordinate X, Y. I'm being explicit in assigning to
//X and Y properties.
public static Coordinate Mercator(double latitude, double longitude)
{
    double siny = Bound(Math.Sin(DegreesToRadians(latitude)), -.9999, .9999);

    Coordinate c = new Coordinate(0,0);
    c.X = OriginX + longitude*PixelsPerLonDegree;
    c.Y = OriginY + .5 * Math.Log((1 + siny) / (1 - siny)) * -PixelsPerLonRadian;

    return c;
}

//From World Coordinate X, Y to Lat, Lon. I'm being explicit in assigning to
//Latitude and Longitude properties.
public static Coordinate InverseMercator(double x, double y)
{      
    Coordinate c = new Coordinate(0, 0);

    c.Longitude = (x - OriginX) / PixelsPerLonDegree;
    double latRadians = (y - OriginY) / -PixelsPerLonRadian;
    c.Latitude = RadiansToDegrees(Math.Atan(Math.Sinh(latRadians)));

    return c;
}

You can check the original javascript code for more detailed comments.

I tested it out by manually approximating the boundaries and then comparing to the answer the code gave. My manual approximations for the NE corner was (51.15501, 4.796695) and the code spit out (51.155005..., 4.797038...) which seems pretty close. SW corner approximate was (51.154572, 4.796007), code spit out (51.154574..., 4.796006...).

This was a fun one, I hope this helps!

EDIT: Realized I didn't include the new GetBounds function:

public static MapCoordinates GetBounds(Coordinate center, int zoom, int mapWidth, int mapHeight)
{
    var scale = Math.Pow(2, zoom);

    var centerWorld = Mercator(center.Latitude, center.Longitude);
    var centerPixel = new Coordinate(0, 0);
    centerPixel.X = centerWorld.X * scale;
    centerPixel.Y = centerWorld.Y * scale;

    var NEPixel = new Coordinate(0, 0);
    NEPixel.X = centerPixel.X + mapWidth / 2.0;
    NEPixel.Y = centerPixel.Y - mapHeight / 2.0;

    var SWPixel = new Coordinate(0, 0);
    SWPixel.X = centerPixel.X - mapWidth / 2.0;
    SWPixel.Y = centerPixel.Y + mapHeight / 2.0;

    var NEWorld = new Coordinate(0, 0);
    NEWorld.X = NEPixel.X / scale;
    NEWorld.Y = NEPixel.Y / scale;

    var SWWorld = new Coordinate(0, 0);
    SWWorld.X = SWPixel.X / scale;
    SWWorld.Y = SWPixel.Y / scale;

    var NELatLon = InverseMercator(NEWorld.X, NEWorld.Y);
    var SWLatLon = InverseMercator(SWWorld.X, SWWorld.Y);

    return new MapCoordinates() { NorthEast = NELatLon, SouthWest = SWLatLon };
}

Just remember to make sure you've got the Latitude and Longitude in there right:

var result = GoogleMapsAPI.GetBounds(new Coordinate(51.15479, 4.79635), 20, 512, 512);

I know the code is not the greatest, but I hope it is clear.

Nancee answered 25/9, 2014 at 21:44 Comment(8)
Thank you very much for the well written answer, sadly indeed it's 2 years too late :)Indigestive
I'm a lot off unless I multiply Math.Pow(2, zoom) with 2. I also compared to other sources and tried all small variations. Any idea why that is?Ohaus
One note for other people, I had a request with size=1024x1024. It gave me an image of 1080x1080 which I was not aware of! So I did bounds checking on a to small region. Resulting in a to small region...Ohaus
There are two class / structures missing from the C# code... Coordinate and MapCoordinatesOrit
clankill3r sorry I didn't see your comments, but without code I can only guess that either there's a mistake in what I have up there or maybe your zoom is somehow off by 1? Jay Dubal I think I was just using the classes included in the original question.Nancee
I'm using the same code as in your example. But I'm getting different results than you are. 'var result = GoogleMapsAPI.GetBounds(Coordinates, 20, 512, 512);' Is giving me SouthWest: 51.1544456481934, 4.79600788170098 NorthEast: 51.1551322937012, 4.79669212270523. Which translate to the following positions SW NECroesus
It would help me a ton if you could elaborate on any other steps you might have taken or suggestions you have for me. I'm hitting a brick wall here and I have to pass it somehow. If you need any more info from my side please let me know. Here is the complete class pastebin.com/Ab2J8GcrCroesus
More info on the problem hereCroesus

© 2022 - 2024 — McMap. All rights reserved.