Форматирование от двух до широты / долготы в удобочитаемом формате - PullRequest
7 голосов
/ 22 декабря 2010

Если формула для преобразования широты или долготы в двойное равно

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

, тогда какова формула для анализа градусов, минут, секунд из двойного?

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

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

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

Пример координат:

широта: 43.81234123

долгота: -119.8374747

Окончательный код для преобразования туда и обратно, еще раз спасибо Питеру и Джеймсу за ответ.Мне пришлось преобразовать значение в десятичную, потому что это используется в Silverlight и Math.Truncate (double) недоступен):

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
}

Unit Test (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);
    }
}

Ответы [ 5 ]

9 голосов
/ 22 декабря 2010
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;
    //...
}

РЕДАКТИРОВАТЬ

Я вернулся к этому из-за недавнего повышения.Вот версия DRY-er с параметром Value, переименованным для отражения наиболее распространенного соглашения о кодировании, в котором параметры начинаются со строчных букв:

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 голосов
/ 22 декабря 2010
#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
1 голос
/ 22 декабря 2010

В дополнение к разбору градуса, минут, секунд (арифметики с точностью до 60) вы также можете иметь дело со знаком преобразования двойных чисел в «север / юг» для широты и «восток / запад».для долготы.

Довольно стандартно отождествлять положительные градусы широты с северным полушарием и отрицательные градусы широт с южным полушарием.Здесь, в Западном полушарии, также принято принимать положительные градусы долготы для обозначения градусов к западу от гринвичского меридиана и наоборот отрицательные градусы долготы для обозначения градусов к востоку от этого меридиана.Однако предпочтительное соглашение для этого является противоположным, принимая градусы к востоку от гринвичского меридиана как отрицательные.Возможно, вы захотите проконсультироваться с вашим клиентом / проанализировать дизайн приложения, чтобы определить, какой выбор применим к этому преобразованию.

Обратите также внимание, что разрыв долготы в ± 180 является причиной для осторожности при преобразовании координат, которая может возникнуть в результатерасчеты.Если преобразование не предназначено для обработки развертки на 180-градусном меридиане, то, скорее всего, следует исключение для таких входных данных.Конечно, проектное решение должно быть задокументировано в любом случае.

Конечно, широты вне диапазона ± 90 ° являются ошибками при вводе.

Добавлено: Учитывая вышеупомянутые различия в разбореширота и долгота, проблемы, которые лучше всего решать в различных подпрограммах ParseLatitude и ParseLongitude, мы можем использовать общую утилиту для преобразования из двойного в градусы / минуты / секунды.

Я не уверен, чтоцелевой язык должен быть здесь, поэтому я написал что-то в простом ванильном C:

&#35;include <math.h>
<br> void double2DegMinSec(double angle, int &#42;Sign, int &#42;Deg, int &#42;Min, double &#42;Sec)<br> { /&#42; extract radix 60 Degrees/Minutes/Seconds from "angle" &#42;/ </p> <pre><code> 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 и ParseLongitude должныуправлять преобразованием знака угла в соответствующее географическое обозначение, но я включил аргумент Знак, который позволит выполнить проверку знака после преобразования (хотя было бы хорошо, если бы преобразование вызывалось только когда-либос неотрицательными углами).

Я сделал функцию double2DegMinSec возвращаемым типом void.Таким образом, результаты должны быть возвращены через его формальные аргументы типа указатель на int и указатель на double (в случае секунд Sec, которые могут иметь дробную часть).

Вызов преобразования в C может быть выполнен следующим образом:
<br> double longitude = -119.8374747;<br> int Sign, Degrees, Minutes;<br> double Seconds; </p> <pre><code>double2DegMinSec(longitude, &Sign, &Degrees, &Minutes, &Seconds);

В C ++ мы сделали бы синтаксис вызова немного хитрым, используя вместо ссылки указатели по ссылке.

1 голос
/ 22 декабря 2010

Я написал класс на C #, который делает многое из этого.Возможно, это полезно, в противном случае вы можете проверить реализацию:

http://code.google.com/p/exif-utils/source/browse/trunk/ExifUtils/ExifUtils/GpsCoordinate.cs

0 голосов
/ 07 апреля 2011

При умножении вы получите ошибки преобразования, не заметив этого, я заметил это при отображении точек на карте.Вам нужно будет принять во внимание наклон и другие переменные, например:

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

Я предполагаю, что вы используете значения Mercator в качестве двойного представления.Чтобы преобразовать значение Меркатора обратно в правильные значения долготы / широты, просто используйте обратное:

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

Это помогло мне.

...