Pregunta

Tengo esta imagen: http://imgur.com/99tsz.png. Un mapa del Reino Unido (que no incluye Irlanda del Sur).

He logrado obtener una latitud y longitud con éxito y trazarla en este mapa tomando la longitud más a la izquierda y la longitud más derecha del Reino Unido y usándolos para resolver dónde poner el punto en el mapa.

Este es el código (para su uso en Processing.js pero podría usarse como JS o cualquier cosa):

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

}

Sin embargo, no he podido implementar una proyección de Mercator en estos valores. Las parcelas son razonablemente precisas, pero no son lo suficientemente buenas y esta proyección lo resolvería.

No puedo entender cómo hacerlo. Todos los ejemplos que encuentro explican cómo hacerlo para todo el mundo. Este es un buen recurso de ejemplos que explican cómo implementar la proyección, pero no he podido hacer que funcione.

Otro recurso es el Puntos extremos del Reino Unido donde obtuve los valores de latitud y longitud del cuadro delimitador alrededor del Reino Unido. También están aquí:

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

Si alguien pudiera ayudarme con esto, ¡lo agradecería mucho!

Gracias

¿Fue útil?

Solución

Creo que vale la pena tener en cuenta que no todos los mapas planos son proyecciones de Mercator. Sin saber más sobre ese mapa en particular, es difícil estar seguro. Puede encontrar que la mayoría de los mapas de una pequeña área del mundo tienen más probabilidades de ser un cónico Tipo de proyección, donde el área de interés en el mapa es "más plana" de lo que estaría en una proyección global de Mercator. Esto es especialmente más importante a medida que se aleja del ecuador (y el Reino Unido está lo suficientemente lejos como para que importe).

Es posible que pueda acercarse "lo suficientemente cerca" de los cálculos que está intentando, pero para la mejor precisión, es posible que desee usar un mapa con una proyección bien definida o crear su propio mapa.

Otros consejos

Escribí una función que hace exactamente lo que estabas buscando. Sé que es un poco tarde, pero tal vez hay otras personas interesadas.

Necesita un mapa que sea una proyección de Mercator y necesita conocer las posiciones LAT / LON de su mapa. Obtienes excelentes mapas de mercator personalizados con posiciones LAT / LON coincidentes perfectas de Tilemill que es un software gratuito de Mapa!

Estoy usando este script y lo probé con algunas posiciones de Google Earth. Funcionó perfecto en un nivel de píxeles. En realidad, no probé esto en mapas diferentes o más grandes. ¡Espero que te ayude!

Raphael;)

<?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];

?>

Aquí está la imagen que creé con Tilemill y que usé en este ejemplo: map image

Además de lo que Raphael Wichmann ha publicado (¡gracias, por cierto!), Aquí está la función inversa, en 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);
}

He convertido el código PHP proporcionado por Raphael a JavaScript y puedo confirmar que funcionó y este código funciona yo mismo. Todo crédito a Raphael.

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

Aquí hay otra implementación de JavaScript. Esta es una simplificación de la solución de @Rob Willet anterior. En lugar de requerir valores calculados como parámetros para la función, solo requiere valores esenciales y calcula todo de ellos:

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
}

Sé que la pregunta se hizo hace un tiempo, pero la biblioteca ProJ4JS es ideal para transformarse entre diferentes proyecciones de mapas en JavaScript.

Los mapas del Reino Unido tienden a usar la red nacional de OSGB, que se basa en una proyección transversal de Mercator. Es decir. Como un mercator convencional pero cumplió 90 grados, de modo que el "ecuador" se convierte en meridiano.

@Xarinko Actionscript Spippet en JavaScript (con algunos valores de prueba)

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)

Si desea evitar algunos de los aspectos más desordenados de las proyecciones LAT/GNL intrínsecas a Proj4js, puede usar D3, que ofrece muchas proyecciones y se reproduce maravillosamente. Aquí hay un ejemplo interactivo de varios sabores de proyecciones azimutales. Prefiero Albers para los mapas de EE. UU.

Si D3 no es una opción de usuario final, por ejemplo, debe admitir IE 7/8, puede renderizar en D3 y luego enganchar las coordenadas XY del archivo SVG resultante que genera D3. Luego puedes representar esas coordenadas XY en Raphael.

Esta función funciona muy bien para mí porque quiero definir el mafeight basado en el mapa que quiero trazar. Estoy generando mapas PDF. Todo lo que necesito hacer es pasar el Max Lat, Min Lon del mapa, y devuelve el tamaño de los píxeles para el mapa como [altura, ancho].

Convergeotopixel (maxlatitude, maxlongitude)

Una nota en el paso final en el que se establece $ y, no reste el cálculo del mapeo de Mapheight si su sistema de coordenadas 'XY' comienza en la parte inferior/izquierda, como con PDFS, esto invertirá el mapa.

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

Implementación de 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)
    };
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top