2009-07-22 9 views
8

Ho una mappa di proiezione Mercator come JPEG e vorrei sapere come correlare una determinata coordinata x, y alla sua latitudine e longitudine. Ho esaminato la funzione Gudermannian ma sinceramente non capisco come prendere quella funzione e applicarla. Vale a dire, quale input si aspetta? L'implementazione che ho trovato (JavaScript) sembra prendere un intervallo tra -PI e PI, ma qual è la correlazione tra il mio valore y in pixel e quell'intervallo?Come posso ottenere latitudine, longitudine da x, y su una mappa Mercator (JPEG)?

Inoltre, ho trovato questa funzione che prende una latitudine e restituisce la tessera per Google Maps, che utilizza anche Mercator. Sembrerebbe che se sapessi come invertire questa funzione, sarei molto vicino ad avere la mia risposta.

/*<summary>Get the vertical tile number from a latitude 
using Mercator projection formula</summary>*/ 

    private int getMercatorLatitude(double lati) 
    { 
     double maxlat = Math.PI; 

     double lat = lati; 

     if (lat > 90) lat = lat - 180; 
     if (lat < -90) lat = lat + 180; 

     // conversion degre=>radians 
     double phi = Math.PI * lat/180; 

     double res; 
     //double temp = Math.Tan(Math.PI/4 - phi/2); 
     //res = Math.Log(temp); 
     res = 0.5 * Math.Log((1 + Math.Sin(phi))/(1 - Math.Sin(phi))); 
     double maxTileY = Math.Pow(2, zoom); 
     int result = (int)(((1 - res/maxlat)/2) * (maxTileY)); 

     return (result); 
    } 
+0

Se non ricordo male, Google utilizza una proiezione equirettangolare, non il Mercator. –

+0

Sia Virtual Earth che Google utilizzano Mercator. –

+1

Inoltre, la massima latitudine utile quando si usa Mercator non è di + -90 gradi - è di circa + -85.05112878 gradi. Il valore è infinito ai poli, quindi devi tapparlo e ignorare i poli. –

risposta

7

Ecco un codice per te ... Fammi sapere se hai bisogno di ulteriori spiegazioni.

/// <summary> 
    /// Calculates the Y-value (inverse Gudermannian function) for a latitude. 
    /// <para><see cref="http://en.wikipedia.org/wiki/Gudermannian_function"/></para> 
    /// </summary> 
    /// <param name="latitude">The latitude in degrees to use for calculating the Y-value.</param> 
    /// <returns>The Y-value for the given latitude.</returns> 
    public static double GudermannianInv(double latitude) 
    { 
     double sign = Math.Sign(latitude); 
     double sin = Math.Sin(latitude * RADIANS_PER_DEGREE * sign); 
     return sign * (Math.Log((1.0 + sin)/(1.0 - sin))/2.0); 
    } 

    /// <summary> 
    /// Returns the Latitude in degrees for a given Y. 
    /// </summary> 
    /// <param name="y">Y is in the range of +PI to -PI.</param> 
    /// <returns>Latitude in degrees.</returns> 
    public static double Gudermannian(double y) 
    { 
     return Math.Atan(Math.Sinh(y)) * DEGREES_PER_RADIAN; 
    } 
+0

Grazie! Provalo adesso. –

+1

Quindi, se ho immagine della mappa un mercator 1588 pixel di altezza, e voglio sapere la latitudine in cui y = 677, vorrei capire 677 in termini di + PI per -PI e chiamare Gudermannian (y_in_terms_of_pi)? Mi rendo conto che è sbagliato, ma si può vedere dove mi sono bloccato mentalmente qui ... –

+0

Per esempio, su una mappa di altezza mercator 1588 pixel, 30.0N è 615 pixel dall'alto. Ma se esprimo 615 in termini di un intervallo lineare da PI (0) a -PI (1588), ottengo 615 -> 0.70824318. E chiamando il precedente Gudermannian (0.70824318) produce 37.5587, non 30.0. –

2

Google, ecc, usare "sferica Mercator", la proiezione di Mercatore utilizzando un modello di terra sferica, piuttosto che le equazioni ellittiche più lenti e più complessi.

Le trasformazioni sono disponibili come parte del codice OpenLayers:

http://docs.openlayers.org/library/spherical_mercator.html

0

Una nota importante quando si esegue un inverso è che non esiste una cosa come "mappa mercator", come è il caso con la maggior parte delle altre proiezioni cartografiche. Ogni mappa mercator esistente è diversa a seconda del valore phi di input. Secondo Wikipedia, google utilizza 85.051129 e altri fornitori di mappe utilizzano 85.05113. Pertanto i valori di input per Gudermannian devono essere ridimensionati in base, ad es. GudermannianInv (85,05,113 mila).

1

Erich Mirabal's answer era completamente corretto (se non completamente completato).

L'ho appena testato utilizzando un "riquadro teorico 256x256 Mercator" (versione a tessera singola di Google di una mappa mondiale).

tile0

Ecco un po 'di più il codice (JavaScript, ma facile da seguire) per chiarire.

Vivo in Australia, ad una latitudine di circa -33 °.

convertRange(
    GudermannianInv(-33), 
    [Math.PI, - Math.PI], 
    [0, 256] 
); 

152,88327883810192

Se si contano 152 pixel verso il basso dalla parte superiore della piastrella, si trova in Australia. Ho anche verificato che questa risposta sia corretta confrontando il risultato con le funzioni note.

A dire il vero, possiamo invertire tale calcolo:

Gudermannian(
    convertRange(
     152.88, 
     [0, 256], 
     [Math.PI, - Math.PI] 
)); 

E siamo tornati -32,99613291758226.

La parte difficile non è in Funzione Gudermanniana, ma nella conversione tra due scale.

Fortunatamente, essendo piuttosto pigro, e odiare questo tipo di problemi di scala, ho già avuto un po 'di funzione per fare che la conversione disordinato per me.

/** 
    * convert number from _n_ of r1[0] .. r1[1] to _n_ of r2[0] .. r2[1] 
    * @example `convertRange(5, [0, 10], [0, 100]) === 50` 
    * 
    * @param {number} value 
    * @param {array<number>} r1 old range 
    * @param {array<number>} r2 new range 
    * @returns {number} value adjusted for new range 
    */ 
    function convertRange(value, r1, r2) { 
     return (value - r1[0]) 
      * (r2[1] - r2[0]) 
      /(r1[1] - r1[0]) 
      + r2[0]; 
    } 

E il JavaScript versioni delle funzioni originali sono naturalmente:

function Gudermannian(y) { 
    return Math.atan(Math.sinh(y)) * (180/Math.PI) 
} 

function GudermannianInv(latitude) 
{ 
    var sign = Math.sign(latitude); 
    var sin = Math.sin(
          latitude 
         * (Math.PI/180) 
         * sign 
    ); 
    return sign * (
     Math.log(
      (1 + sin)/(1 - sin) 
     )/2 
    ); 
}