Google Maps Static API — получение SW и NE по центральной координате

Я загружаю фрагменты карты из Google Maps, используя Static Maps, используя .NET.

Проблема, с которой я сталкиваюсь, заключается в том, что я не знаю, каковы координаты SW и NE возвращаемого изображения.

Я нашел много разных примеров кода, формул, но все они кажутся ошибочными. Это тот, который был ближе всего к правильному ответу. Когда я ввел координаты в Google Maps, оказалось, что это немного не так.

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

Источники:
Как получить границы статики Google карту?
http://www.easywms.com/easywms/?q=zh-hans/node/3612

Загруженное изображение по координате центра:
http://maps.googleapis.com/maps/api/staticmap?center=51.15479,4.79635&zoom=20&size=512x512&sensor=false

Неисправное ПО:
https://maps.google.be/maps?q=51.154545859375,+4.796105859375&hl=ru&ll=51.154763,4.796695&spn=0.000568,0.001635&sll=51.15001387,8&sll=51.15001387,8&ssll=51.15001387,4.796837.,0.00327&t=m&z=20

Неисправный северо-восток:
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,23&ssll=51.154599,4.79660.001136,0.00327&t=m&z=20


person Rik De Peuter    schedule 02.10.2012    source источник


Ответы (1)


РЕДАКТИРОВАТЬ: Вау, я только что понял, что этому вопросу почти два года. Извини за это.

Итак, я думаю, что здесь происходит несколько вещей. Самое главное, что вы не делаете проекцию Меркатора, упомянутую в предоставленных вами ссылках. Вы можете увидеть его в действии с помощью примера кода в Google документы API карт. Я думаю, причина, по которой вы приближаетесь, заключается в том, что коэффициент масштабирования настолько велик на уровне масштабирования 20, что заглушает детали проблемы.

Чтобы получить границы, вам нужно взять центральную широту/долготу, преобразовать его в пиксельные координаты, добавить/вычесть, чтобы получить пиксельные координаты нужных вам углов, а затем преобразовать обратно в широту/долготу. Код в первой ссылке может выполнять проекцию и обратную проекцию. Ниже я перевел его на С#.

В приведенном выше решении вы берете координаты широты/долготы и добавляете/вычитаете мировые координаты (пиксельные координаты/масштаб), поэтому вы объединяете две разные системы координат.

Проблема, с которой я столкнулся, пытаясь понять это, заключается в том, что ваш класс Coordinate немного сбивает с толку. Надеюсь, у меня нет этого в обратном направлении, но похоже, что вы вводите широту и долготу в обратном направлении. Это важно, потому что, помимо прочего, проекция Меркатора отличается в каждом направлении. Ваше сопоставление X/Y также кажется обратным, потому что широта — это ваше положение север/юг, которое должно быть координатой Y при проецировании, а долгота — это ваше положение восток/запад, которое должно быть координатой X при проецировании.

Чтобы найти границы, необходимы три системы координат: долгота/широта, Мировые координаты и координаты пикселей. Процесс таков: широта/долгота центра - (Проекция Меркатора) -> Мировые координаты центра -> Координаты центра пикселей -> Пиксельные координаты СВ/ЮЗ -> Мировые координаты СВ/ЮЗ - (обратная проекция Меркатора) -> СВ/ЮЗ широта/долгота .

Вы находите координаты пикселей, добавляя/вычитая размеры изображения/2. Однако система координат в пикселях начинается с верхнего левого угла, поэтому, чтобы получить северо-восточный угол, вам нужно прибавить ширину/2 к x и вычесть высоту/2 из y. Для юго-западного угла вам нужно вычесть ширину/2 из x и добавить высоту/2 к y.

Вот код проекции (как часть вашего класса GoogleMapsAPI) на С#, перевод из первой ссылки выше 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;
}

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

Я проверил это, вручную приблизив границы, а затем сравнив с ответом, который дал код. Мои ручные приближения для северо-восточного угла были (51.15501, 4.796695), и код выдал (51.155005..., 4.797038...) что кажется довольно близким. Приблизительный угол юго-запада был (51.154572, 4.796007), код выдал (51.154574..., 4.796006...).

Это было весело, я надеюсь, что это поможет!

РЕДАКТИРОВАТЬ: понял, что я не включил новую функцию GetBounds:

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

Просто не забудьте убедиться, что вы правильно указали Широту и Долготу:

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

Я знаю, что код не самый лучший, но я надеюсь, что он понятен.

person peterjb    schedule 25.09.2014
comment
Большое спасибо за хорошо написанный ответ, к сожалению, на самом деле это 2 года слишком поздно :) - person Rik De Peuter; 02.10.2014
comment
Я сильно ошибаюсь, если только не умножаю Math.Pow(2, zoom) на 2. Я также сравнивал с другими источниками и пробовал все небольшие варианты. Любая идея, почему это так? - person clankill3r; 09.09.2015
comment
Одно замечание для других людей, у меня был запрос с size=1024x1024. Это дало мне изображение 1080x1080, о котором я не знал! Поэтому я проверил границы небольшого региона. В результате получился небольшой регион... - person clankill3r; 09.09.2015
comment
В коде С# отсутствуют два класса/структуры... Координаты и MapCoordinates - person Jay Dubal; 29.01.2016
comment
clankill3r извините, я не видел ваших комментариев, но без кода я могу только догадываться, что либо у меня там ошибка, либо, может быть, ваш зум как-то сбит на 1? Джей Дубал: Я думаю, что просто использовал классы, включенные в исходный вопрос. - person peterjb; 30.01.2016
comment
Я использую тот же код, что и в вашем примере. Но я получаю другие результаты, чем вы. 'var result = GoogleMapsAPI.GetBounds(Координаты, 20, 512, 512);' Дает мне Юго-Запад: 51.1544456481934, 4.79600788170098 Северо-Восток: 51.1551322937012, 4.79669212270523. Которые переводятся в следующие позиции: SW NE - person Leniaal; 03.05.2016
comment
Мне бы очень помогло, если бы вы могли рассказать о любых других шагах, которые вы могли предпринять, или предложениях, которые у вас есть для меня. Здесь я натыкаюсь на кирпичную стену, и мне нужно как-то ее преодолеть. Если вам нужна дополнительная информация с моей стороны, пожалуйста, дайте мне знать. Вот полный класс pastebin.com/Ab2J8Gcr - person Leniaal; 03.05.2016
comment
Подробнее о проблеме здесь - person Leniaal; 03.05.2016