Расчет долготы и широты Меркатора до x и y на обрезанной карте (Великобритании) - PullRequest
15 голосов
/ 20 января 2010

У меня есть это изображение: http://imgur.com/99tSz.png. Карта Великобритании (не включая Южную Ирландию).

Мне удалось получить широту и долготу и нанести их на эту карту, взяв крайнюю левую долготу и крайнюю правую долготу Великобритании и используя их для определения точки на карте.

Это код (для использования в Processing.js, но может использоваться как js или как угодно):

// Size of the map
int width = 538;
int height = 811;
// X and Y boundaries
float westLong = -8.166667;
float eastLong = 1.762833;
float northLat = 58.666667;
float southLat = 49.95;

void drawPoint(float latitude, float longitude){

 fill(#000000);

 x = width * ((westLong-longitude)/(westLong-eastLong));
 y = (height * ((northLat-latitude)/(northLat-southLat)));

 console.log(x + ", " + y);
 ellipseMode(RADIUS);
 ellipse(x, y, 2, 2);    

}

Однако мне не удалось реализовать проекцию Меркатора для этих значений. Графики достаточно точны, но они недостаточно хороши, и эта проекция решит эту проблему.

Я не могу понять, как это сделать. Все примеры, которые я нахожу, объясняют, как это сделать для всего мира. Этот является хорошим источником примеров, объясняющих, как реализовать проекцию, но я не смог заставить его работать.

Другим ресурсом являются Крайние точки Соединенного Королевства , где я получил значения широты и долготы ограничительной рамки вокруг Великобритании. Они также здесь:

northLat = 58.666667; 
northLong = -3.366667; 
eastLat = 52.481167; 
eastLong = 1.762833; 
southLat = 49.95;
southLong = -5.2; 
westLat = 54.45;
westLong = -8.166667;

Если бы кто-нибудь мог мне помочь с этим, я был бы очень признателен!

Спасибо

Ответы [ 10 ]

39 голосов
/ 01 мая 2012

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

Вам нужна карта, которая является проекцией Меркатора, и вам необходимо знать ее широту и долготу. Вы получаете отличные настроенные карты меркатора с идеально подходящими позициями широт / долгот от TileMill , который является бесплатным программным обеспечением от MapBox !

Я использую этот скрипт и проверил его на некоторых позициях Google Earth. Он отлично работал на уровне пикселей. На самом деле я не проверял это на разных или больших картах. Я надеюсь, что это поможет вам!

Рафаэль;)

<?php

$mapWidth = 1500;
$mapHeight = 1577;

$mapLonLeft = 9.8;
$mapLonRight = 10.2;
$mapLonDelta = $mapLonRight - $mapLonLeft;

$mapLatBottom = 53.45;
$mapLatBottomDegree = $mapLatBottom * M_PI / 180;

function convertGeoToPixel($lat, $lon)
{
    global $mapWidth, $mapHeight, $mapLonLeft, $mapLonDelta, $mapLatBottom, $mapLatBottomDegree;

    $x = ($lon - $mapLonLeft) * ($mapWidth / $mapLonDelta);

    $lat = $lat * M_PI / 180;
    $worldMapWidth = (($mapWidth / $mapLonDelta) * 360) / (2 * M_PI);
    $mapOffsetY = ($worldMapWidth / 2 * log((1 + sin($mapLatBottomDegree)) / (1 - sin($mapLatBottomDegree))));
    $y = $mapHeight - (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);

    return array($x, $y);
}

$position = convertGeoToPixel(53.7, 9.95);
echo "x: ".$position[0]." / ".$position[1];

?>

Вот изображение, которое я создал с помощью TileMill и которое я использовал в этом примере: map image

10 голосов
/ 10 ноября 2012

В дополнение к тому, что написал Рафаэль Вихманн (спасибо, кстати!), здесь есть обратная функция в actionscript:

function convertPixelToGeo(tx:Number, ty:Number):Point
{   
    /* called worldMapWidth in Raphael's Code, but I think that's the radius since it's the map width or circumference divided by 2*PI  */   
    var worldMapRadius:Number = mapWidth / mapLonDelta * 360/(2 * Math.PI);     
    var mapOffsetY:Number = ( worldMapRadius / 2 * Math.log( (1 + Math.sin(mapLatBottomRadian) ) / (1 - Math.sin(mapLatBottomRadian))  ));
    var equatorY:Number = mapHeight + mapOffsetY;   
    var a:Number = (equatorY-ty)/worldMapRadius;

    var lat:Number = 180/Math.PI * (2 * Math.atan(Math.exp(a)) - Math.PI/2);
    var long:Number = mapLonLeft+tx/mapWidth*mapLonDelta;
    return new Point(lat,long);
}
9 голосов
/ 05 декабря 2014

Я преобразовал код PHP, предоставленный Raphael, в JavaScript и могу подтвердить, что он работает, и этот код работает сам. Вся заслуга Рафаэлю.

/*
var mapWidth = 1500;
var mapHeight = 1577;

var mapLonLeft = 9.8;
var mapLonRight = 10.2;
var mapLonDelta = mapLonRight - mapLonLeft;

var mapLatBottom = 53.45;
var mapLatBottomDegree = mapLatBottom * Math.PI / 180;
*/

function convertGeoToPixel(latitude, longitude ,
                           mapWidth , // in pixels
                           mapHeight , // in pixels
                           mapLonLeft , // in degrees
                           mapLonDelta , // in degrees (mapLonRight - mapLonLeft);
                           mapLatBottom , // in degrees
                           mapLatBottomDegree) // in Radians
{
    var x = (longitude - mapLonLeft) * (mapWidth / mapLonDelta);

    latitude = latitude * Math.PI / 180;
    var worldMapWidth = ((mapWidth / mapLonDelta) * 360) / (2 * Math.PI);
    var mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomDegree)) / (1 - Math.sin(mapLatBottomDegree))));
    var y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitude)) / (1 - Math.sin(latitude)))) - mapOffsetY);

    return { "x": x , "y": y};
}
7 голосов
/ 20 января 2010

Я думаю, стоит помнить, что не все плоские карты являются проекциями Меркатора. Не зная больше об этой карте, трудно быть уверенным. Вы можете обнаружить, что большинство карт небольшой области мира с большей вероятностью представляют собой проекцию типа конический , где интересующая область на карте "более плоская", чем на глобальной проекции Меркатора. Это тем более важно, чем дальше вы уходите от экватора (а Великобритания достаточно далеко, чтобы это имело значение).

Возможно, вам удастся "приблизиться", используя расчеты, которые вы пытаетесь, но для большей точности вы можете использовать карту с четко определенной проекцией или создать собственную карту.

5 голосов
/ 09 мая 2017

Вот еще одна реализация Javascript. Это упрощение решения @Rob Willet, описанного выше. Вместо того, чтобы требовать вычисленные значения в качестве параметров для функции, он только требует основных значений и вычисляет все из них:

function convertGeoToPixel(latitude, longitude,
                  mapWidth, // in pixels
                  mapHeight, // in pixels
                  mapLngLeft, // in degrees. the longitude of the left side of the map (i.e. the longitude of whatever is depicted on the left-most part of the map image)
                  mapLngRight, // in degrees. the longitude of the right side of the map
                  mapLatBottom) // in degrees.  the latitude of the bottom of the map
{
    const mapLatBottomRad = mapLatBottom * Math.PI / 180
    const latitudeRad = latitude * Math.PI / 180
    const mapLngDelta = (mapLngRight - mapLngLeft)

    const worldMapWidth = ((mapWidth / mapLngDelta) * 360) / (2 * Math.PI)
    const mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomRad)) / (1 - Math.sin(mapLatBottomRad))))

    const x = (longitude - mapLngLeft) * (mapWidth / mapLngDelta)
    const y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitudeRad)) / (1 - Math.sin(latitudeRad)))) - mapOffsetY)

    return {x, y} // the pixel x,y value of this point on the map image
}
4 голосов
/ 19 ноября 2010

Я знаю, что вопрос задавался некоторое время назад, но библиотека Proj4JS идеально подходит для преобразования различных проекций карт в JavaScript.

Карты Великобритании, как правило, используют национальную сетку OSGB, основанную на поперечной проекции Меркатора. То есть. как обычный Меркатор, но повернутый на 90 градусов, так что «экватор» становится меридианом.

3 голосов
/ 18 марта 2015

@ Фрагмент Xarinko Actionscript в Javascript (с некоторыми значениями тестирования)

var mapWidth = 1500;
var mapHeight = 1577;

var mapLonLeft = 9.8;
var mapLonRight = 10.2;
var mapLonDelta = mapLonRight - mapLonLeft;

var mapLatBottom = 53.45;
var mapLatBottomRadian = mapLatBottom * Math.PI / 180;



function convertPixelToGeo(tx, ty)
{   
    /* called worldMapWidth in Raphael's Code, but I think that's the radius since it's the map width or circumference divided by 2*PI  */   
    var worldMapRadius = mapWidth / mapLonDelta * 360/(2 * Math.PI);     
    var mapOffsetY = ( worldMapRadius / 2 * Math.log( (1 + Math.sin(mapLatBottomRadian) ) / (1 - Math.sin(mapLatBottomRadian))  ));
    var equatorY = mapHeight + mapOffsetY;   
    var a = (equatorY-ty)/worldMapRadius;

    var lat = 180/Math.PI * (2 * Math.atan(Math.exp(a)) - Math.PI/2);
    var long = mapLonLeft+tx/mapWidth*mapLonDelta;
    return [lat,long];
}

convertPixelToGeo(241,444)
0 голосов
/ 16 января 2019

C # реализация:

private Point ConvertGeoToPixel(
    double latitude, double longitude, // The coordinate to translate
    int imageWidth, int imageHeight, // The dimensions of the target space (in pixels)
    double mapLonLeft, double mapLonRight, double mapLatBottom // The bounds of the target space (in geo coordinates)
) {
    double mapLatBottomRad = mapLatBottom * Math.PI / 180;
    double latitudeRad = latitude * Math.PI / 180;

    double mapLonDelta = mapLonRight - mapLonLeft;
    double worldMapWidth = (imageWidth / mapLonDelta * 360) / (2 * Math.PI);
    double mapOffsetY = worldMapWidth / 2 * Math.Log((1 + Math.Sin(mapLatBottomRad)) / (1 - Math.Sin(mapLatBottomRad)));

    double x = (longitude - mapLonLeft) * (imageWidth / mapLonDelta);
    double y = imageHeight - ((worldMapWidth / 2 * Math.Log((1 + Math.Sin(latitudeRad)) / (1 - Math.Sin(latitudeRad)))) - mapOffsetY);

    return new Point()
    {
        X = Convert.ToInt32(x),
        Y = Convert.ToInt32(y)
    };
}
0 голосов
/ 30 января 2016

Эта функция отлично работает для меня, потому что я хочу определить mapHeight на основе карты, которую я хочу построить. Я создаю PDF-карты. Все, что мне нужно сделать, это передать максимальный лат на карте, минимальный долг, и он возвращает размер пикселей для карты как [высота, ширина].

convertGeoToPixel (maxlatitude, maxlongitude)

Одна заметка на последнем шаге, где установлен $ y, не вычитайте вычисление из mapHeight, если ваша система координат 'xy' начинается внизу / слева, как в случае PDF, это инвертирует карту.

$y =  (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);
0 голосов
/ 10 ноября 2012

Если вы хотите избежать некоторых более сложных аспектов проекций широты / ширины, свойственных Proj4JS, вы можете использовать D3, который предлагает множество встроенных проекций и прекрасно отображает. Вот интерактивный пример нескольких разновидностей азимутальных проекций. Я предпочитаю Альберса для карт США.

Если D3 не является опцией конечного пользователя - скажем, вам нужно поддерживать IE 7/8 - вы можете выполнить рендеринг в D3 и затем получить координаты xy из результирующего файла SVG, который генерирует D3. Затем вы можете отобразить эти координаты xy в Рафаэле.

...