2010-12-21 18 views
6

Si la fórmula para convertir latitud o longitud de doblar esEl formato doble a la latitud/longitud formato legible por humanos

((Degree) + (Minute)/60 + (Second)/3600) * ((South || West) ? -1 : 1)

entonces ¿cuál es la fórmula para el análisis sintáctico grados, minutos, segundos desde una doble?

Tendría sentido tener dos métodos separados para analizar la latitud y la longitud, pero no estoy seguro de cómo analizar los grados, los minutos, los segundos del doble.

ParseLatitude(double value) 
{ 
    //value is South if negative, else is North. 
} 

ParseLongitude(double value) 
{ 
    //value is West if negative, else is East. 
} 

Ejemplo coordina:

Latitud: 43.81234123

Longitud: -119.8374747

El código final para convertir un lado a otro, gracias de nuevo a Pedro, a Santiago para la respuesta. Tenía que convertir valor a decimal porque esto se está utilizando en Silverlight y Math.Truncate (doble) no está disponible):

public class Coordinate 
{ 
    public double Degrees { get; set; } 
    public double Minutes { get; set; } 
    public double Seconds { get; set; } 
    public CoordinatesPosition Position { get; set; } 

    public Coordinate() { } 
    public Coordinate(double value, CoordinatesPosition position) 
    { 
     //sanity 
     if (value < 0 && position == CoordinatesPosition.N) 
      position = CoordinatesPosition.S; 
     //sanity 
     if (value < 0 && position == CoordinatesPosition.E) 
      position = CoordinatesPosition.W; 
     //sanity 
     if (value > 0 && position == CoordinatesPosition.S) 
      position = CoordinatesPosition.N; 
     //sanity 
     if (value > 0 && position == CoordinatesPosition.W) 
      position = CoordinatesPosition.E; 

     var decimalValue = Convert.ToDecimal(value); 

     decimalValue = Math.Abs(decimalValue); 

     var degrees = Decimal.Truncate(decimalValue); 
     decimalValue = (decimalValue - degrees) * 60; 

     var minutes = Decimal.Truncate(decimalValue); 
     var seconds = (decimalValue - minutes) * 60; 

     Degrees = Convert.ToDouble(degrees); 
     Minutes = Convert.ToDouble(minutes); 
     Seconds = Convert.ToDouble(seconds); 
     Position = position; 
    } 
    public Coordinate(double degrees, double minutes, double seconds, CoordinatesPosition position) 
    { 
     Degrees = degrees; 
     Minutes = minutes; 
     Seconds = seconds; 
     Position = position; 
    } 
    public double ToDouble() 
    { 
     var result = (Degrees) + (Minutes)/60 + (Seconds)/3600; 
     return Position == CoordinatesPosition.W || Position == CoordinatesPosition.S ? -result : result; 
    } 
    public override string ToString() 
    { 
     return Degrees + "º " + Minutes + "' " + Seconds + "'' " + Position; 
    } 
} 

public enum CoordinatesPosition 
{ 
    N, E, S, W 
} 

Prueba de la unidad (nUnit)

[TestFixture] 
public class CoordinateTests 
{ 
    [Test] 
    public void ShouldConvertDoubleToCoordinateAndBackToDouble() 
    { 
     const double baseLatitude = 43.81234123; 
     const double baseLongitude = -119.8374747; 

     var latCoordN = new Coordinate(baseLatitude, CoordinatesPosition.N); 
     var latCoordS = new Coordinate(baseLatitude, CoordinatesPosition.S); 
     var lonCoordE = new Coordinate(baseLongitude, CoordinatesPosition.E); 
     var lonCoordW = new Coordinate(baseLongitude, CoordinatesPosition.W); 

     var convertedLatitudeS = latCoordS.ToDouble(); 
     var convertedLatitudeN = latCoordN.ToDouble(); 
     var convertedLongitudeW = lonCoordW.ToDouble(); 
     var convertedLongitudeE = lonCoordE.ToDouble(); 

     Assert.AreEqual(convertedLatitudeS, convertedLatitudeN); 
     Assert.AreEqual(baseLatitude, convertedLatitudeN); 
     Assert.AreEqual(convertedLongitudeE, convertedLongitudeW); 
     Assert.AreEqual(baseLongitude, convertedLongitudeE); 
    } 
} 

Respuesta

9
ParseLatitude(double Value) 
{ 
    var direction = Value < 0 ? Direction.South : Direction.North; 

    Value = Math.Abs(Value); 

    var degrees = Math.Truncate(Value); 

    Value = (Value - degrees) * 60;  //not Value = (Value - degrees)/60; 

    var minutes = Math.Truncate(Value); 
    var seconds = (Value - minutes) * 60; //not Value = (Value - degrees)/60; 
    //... 
} 

ParseLongitude(double Value) 
{ 
    var direction = Value < 0 ? Direction.West : Direction.East; 

    Value = Math.Abs(Value); 

    var degrees = Math.Truncate(Value); 

    Value = (Value - degrees) * 60;  //not Value = (Value - degrees)/60; 

    var minutes = Math.Truncate(Value); 
    var seconds = (Value - minutes) * 60; //not Value = (Value - degrees)/60; 
    //... 
} 

EDITAR

volví a esto debido a un upvote reciente. Aquí hay una versión DRY-er, con el parámetro Value cambiado el nombre para reflejar la convención de codificación más común, en la que los parámetros comienzan con letras minúsculas:

ParseLatitude(double value) 
{ 
    var direction = value < 0 ? Direction.South : Direction.North; 
    return ParseLatituteOrLongitude(value, direction); 
} 

ParseLongitude(double value) 
{ 
    var direction = value < 0 ? Direction.West : Direction.East; 
    return ParseLatituteOrLongitude(value, direction); 
} 

//This must be a private method because it requires the caller to ensure 
//that the direction parameter is correct. 
ParseLatitudeOrLongitude(double value, Direction direction) 
{ 
    value = Math.Abs(value); 

    var degrees = Math.Truncate(value); 

    value = (value - degrees) * 60;  //not Value = (Value - degrees)/60; 

    var minutes = Math.Truncate(value); 
    var seconds = (value - minutes) * 60; //not Value = (Value - degrees)/60; 
    //... 
} 
+2

Lo sentimos, el valor debe multiplicarse, no dividirse. –

+0

También votó la suya porque Truncate es el método correcto para usar, en lugar de buscar + y usar Piso/Techo. –

+0

@c_bit: Edité la respuesta. Gracias por corregir mi error. Y para la votación :-) – phoog

1
#include <math.h> 

void ParseLatitude(double Value, bool &north, double &deg, double &min, double &sec) 
{ 
    if (Value < 0) 
    { 
    ParseLatitude(-Value, north, deg, min, sec); 
    north = false; 
    } 
    else 
    { 
    north = true; 
    deg = floor(Value); 
    Value = 60*(Value - deg); 
    min = floor(Value); 
    Value = 60*(Value - min); 
    sec = Value; 
    } 
} 

// ParseLongitude is similar 
+0

¿De acuerdo a la función redonda, redondee desde 0.5 de grado a grado completo? –

+0

Vaya, correcto, eso debería ser floor() - editado. –

+0

¡Gracias Peter (y James)! Lo necesitaba para C#, pero tu fórmula dentro de la declaración else es precisa. Casi seleccioné la respuesta de Phooog ya que tenía 1 voto, pero se está dividiendo y eso hizo que fallaran mis pruebas unitarias. :) –

1

Además de analizar el grado, minutos, segundos (que es simplemente aritmética de radix 60), también puede tratar con signos de los dobles que se convierten en "Norte/Sur" para la latitud y "Este/Oeste" para la longitud.

Es bastante estándar para identificar grados positivos de latitud con el hemisferio norte y grados negativos de latitud con el hemisferio sur. También es común aquí en el Hemisferio Occidental tomar grados de longitud positiva en grados medios al oeste del meridiano de Greenwich y, a la inversa grados negativos de longitud en grados medios al este de ese meridiano. Sin embargo, the preferred convention for this es lo contrario, tomar grados al este del meridiano de Greenwich como negativos. Es posible que desee consultar con su cliente/analizar el diseño de la aplicación para determinar qué opción se aplica a esta conversión.

Tenga en cuenta también que la discontinuidad de longitud en & plusmn; 180 es una causa de cuidado en la conversión de coordenadas que pueden resultar de los cálculos. Si la conversión no tiene la intención de manejar el envolvente en el meridiano 180 °, entonces es probable que se genere una excepción para tales entradas. Por supuesto, la decisión de diseño debe documentarse de cualquier manera.

Ciertamente, las latitudes fuera del & plusmn; 90 ° rango son errores en la entrada.

Agregado: Teniendo en cuenta las diferencias mencionadas anteriormente en la latitud de análisis y longitud, cuestiones que mejor se manejan en los distintos ParseLatitude & rutinas ParseLongitude, podríamos usar una utilidad común para hacer la conversión de doble a grados/minutos/segundos .

no estoy seguro de lo que el idioma de destino debe estar aquí, así que escribió algo en la llanura de vainilla C:

#include <math.h>

void double2DegMinSec(double angle, int *Sign, int *Deg, int *Min, double *Sec)
{ /* extract radix 60 Degrees/Minutes/Seconds from "angle" */

Sign = 1; 

    if (angle < 0.0) /* reduce to case of nonnegative angle */ 
    { 
     Sign = -Sign; 
     angle = -angle; 
    } 

    *Deg = floor(angle); 
    angle -= *Deg; 
    angle *= 60.0; 
    *Min = floor(angle); 
    angle -= *Min; 
    angle *= 60.0; 
    *Sec = angle; 

    return; 
} 

ParseLatitude probable y ParseLongitude debe manejar la conversión de la señal del ángulo de la designación geográfica apropiada, pero he incluido un argumento Sign que permitirá que se realice la comprobación del signo después de la conversión (aunque estaría bien si la conversión solo se llamara con ángulos no negativos).

Hice que la función double2DegMinSec tenga un tipo de vacío de retorno. Los resultados deben devolverse a través de sus argumentos formales de tipo puntero a int y puntero a doble (en el caso de segundos Sec, que podría tener una parte fraccionaria).

llamando a la conversión en C podría hacerse así:

double longitude = -119.8374747;
int Sign, Degrees, Minutes;
double Seconds;

double2DegMinSec(longitude, &Sign, &Degrees, &Minutes, &Seconds); 

En C++ que haría que la sintaxis llamar un poco Glibber mediante una llamada por referencia en lugar de punteros.

0

Con solo multiplicar obtendrá errores de conversión sin darse cuenta, lo noté al mapear los puntos en un mapa. Tendrá que tener en cuenta la pendiente y otras variables, como esto:

public static void GeoToMercator(double xIn, double yIn, out double xOut, out double yOut) 
    { 
     double xArg = xIn/100000, yArg = yIn/100000; 
     xArg = 6371000.0 * Math.PI/180 * xArg; 
     yArg = 6371000.0 * Math.Log(Math.Tan(Math.PI/4 + Math.PI/180 * yArg * 0.5)); 
     xOut = xArg/10000; 
     yOut = yArg/10000; 
    } 

te estoy adivinando está utilizando Mercator valores como la representación doble. Para convertir el valor de Mercator de nuevo en los valores de longitud/latitud correctas, sólo tiene que utilizar a la inversa:

public static void MercatorToGeo(double xIn, double yIn, out double xOut, out double yOut) 
    { 
     double xArg = xIn, yArg = yIn; 
     xArg = 180/Math.PI * xArg/6371000.0; 
     yArg = 180/Math.PI * (Math.Atan(Math.Exp(yArg/6371000.0)) - Math.PI/4)/0.5; 
     xOut = xArg * 10; 
     yOut = yArg * 10; 
    } 

Esto hizo el truco para mí.

Cuestiones relacionadas