Pergunta

Eu tenho esta imagem: http://imgur.com/99tsz.png. Um mapa do Reino Unido (não incluindo a Irlanda do Sul).

Consegui obter uma latitude e longitude com sucesso e plotá -la neste mapa, pegando a longitude mais à esquerda e a longitude mais à direita do Reino Unido e usando -as para descobrir onde colocar o ponto no mapa.

Este é o código (para uso no processing.js, mas pode ser usado como js ou qualquer coisa):

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

}

No entanto, não consegui implementar uma projeção da Mercator nesses valores. As parcelas são razoavelmente precisas, mas não são boas o suficiente e essa projeção a resolveria.

Não consigo descobrir como fazer isso. Todos os exemplos que encontro estão explicando como fazê -lo para o mundo inteiro. este é um bom recurso de exemplos explicando como implementar a projeção, mas não consegui fazê -lo funcionar.

Outro recurso é o Pontos extremos do Reino Unido Onde eu recebi os valores de latitude e longitude da caixa delimitadora ao redor do Reino Unido. Eles também estão aqui:

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

Se alguém pudesse me ajudar com isso, eu apreciaria muito!

Obrigado

Foi útil?

Solução

Eu acho que vale a pena ter em mente que nem todos os mapas planos são projeções da Mercator. Sem saber mais sobre esse mapa em particular, é difícil ter certeza. Você pode achar que a maioria dos mapas de uma pequena área do mundo tem maior probabilidade de ser um cônico Projeção de tipo, onde a área de interesse no mapa é "mais plana" do que seria uma projeção global da Mercator. Isso é especialmente mais importante quanto mais você se afasta do equador (e o Reino Unido está longe o suficiente para que isso importa).

Você pode chegar "perto o suficiente" usando os cálculos que está tentando, mas, com a melhor precisão, você pode usar um mapa com uma projeção bem definida ou criar seu próprio mapa.

Outras dicas

Eu escrevi uma função que faz exatamente o que você estava procurando. Eu sei que é um pouco tarde, mas talvez haja outras pessoas interessadas.

Você precisa de um mapa que seja uma projeção Mercator e precisa conhecer as posições LAT / LON do seu mapa. Você obtém ótimos mapas Mercator personalizados com posições de Lat / Lon com correspondência perfeita de Tilemill que é um software livre de Mapbox!

Estou usando este script e o testei com algumas posições do Google Earth. Funcionou perfeito em um nível de pixel. Na verdade, eu não testei isso em mapas diferentes ou maiores. Espero que ajude você!

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

?>

Aqui está a imagem que eu criei com Tilemill e que usei neste exemplo: map image

Além do que Raphael Wichmann postou (obrigado, a propósito!), Aqui está a função reversa, no 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);
}

Convertei o código PHP fornecido por Raphael em JavaScript e posso confirmar que funcionou e esse código eu mesmo funciona. Todo o 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};
}

Aqui está outra implementação do JavaScript. Esta é uma simplificação da solução do @ROB Willet acima. Em vez de exigir valores calculados como parâmetros para a função, ela requer apenas valores essenciais e calcula tudo a partir deles:

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
}

Sei que a pergunta foi feita há um tempo atrás, mas a biblioteca ProJ4JS é ideal para transformar entre diferentes projeções de mapa em JavaScript.

Os mapas do Reino Unido tendem a usar a grade nacional do OSGB, baseada em uma projeção transversal da Mercator. Ou seja. Como um Mercator convencional, mas girou 90 graus, para que o "equador" se torne um meridiano.

@Xarinko ActionScript Snippet em JavaScript (com alguns valores de teste)

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)

Se você deseja evitar alguns dos aspectos mais confusos das projeções Lat/LNG intrínsecas ao ProJ4Js, você pode usar o D3, que oferece muitas projeções assadas e renderiza lindamente. Aqui está um Exemplo interativo de vários sabores de projeções azimutais. Eu prefiro Albers para mapas dos EUA.

Se o D3 não for uma opção do usuário final-digamos, você precisará suportar o IE 7/8-você pode renderizar no D3 e, em seguida, prender as coordenadas XY do arquivo SVG resultante que o D3 gera. Você pode renderizar essas coordenadas XY em Raphael.

Essa função funciona muito bem para mim, porque quero definir o peso -máfão com base no mapa que quero plotar. Estou gerando mapas PDF. Tudo o que preciso fazer é passar no Max Lat, min LON do mapa e ele retorna o tamanho dos pixels para o mapa como [altura, largura].

convertgetotopixel (maxlatitude, maxlongitude)

Uma nota na etapa final em que $ y está definido, não subtraia o cálculo do MaphEight se o seu sistema de coordenadas 'XY' começar na parte inferior/esquerda, como com PDFs, isso inverterá o mapa.

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

C# Implementação:

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top