Расчеты долготы и широты Mercator на укороченную карту (из Великобритании)

StackOverflow https://stackoverflow.com/questions/2103924

Вопрос

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

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

Это код (для использования в обработке.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);    

}

Тем не менее, я не смог реализовать проекцию Mercator по этим значениям. Заговоры достаточно точны, но они недостаточно хороши, и эта проекция решит его.

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

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

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

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

Спасибо

Это было полезно?

Решение

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

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

Другие советы

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

Вам нужна карта, которая является проекцией Mercator, и вам нужно знать позиции LAT / LON вашей карты. Вы получаете отличные индивидуальные карты Mercator с идеальными соответствующими позициями LAT / LON от Тилемл которое является бесплатным программным обеспечением от 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

В дополнение к тому, что Рафаэль Вичманн опубликовал (кстати, спасибо!), Вот обратная функция, в 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);
}

Я преобразовал код 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};
}

Вот еще одна реализация 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
}

Я знаю, что вопрос был задан некоторое время назад, но библиотека ProJ4JS идеально подходит для преобразования между различными прогнозами карты в JavaScript.

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

@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)

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

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

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

Convertgeotopixel (Maxlatitude, Maxlongity)

Одна примечания на последнем этапе, когда установлен $ y, не вычитайте расчет из Mapheight, если ваша система координат «XY» начинается внизу/слева, как и в PDF, это будет инвертировать карту.

$y =  (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);

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)
    };
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top