7

如果将纬度或经度转换为双精度的公式是

((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

来回转换的最终代码,再次感谢 Peter 和 James 的回答。我不得不将转换为十进制,因为这在 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
}

单元测试 (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);
    }
}
4

5 回答 5

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

编辑

由于最近的一次投票,我回到了这个话题。这是一个 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;
    //...
}
于 2010-12-21T23:46:52.680 回答
2
#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
于 2010-12-21T23:46:29.613 回答
1

我用 C# 编写了一个类,它做了很多这样的事情。也许它很有用,否则您可以查看实现:

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

于 2010-12-21T23:40:42.620 回答
1

除了解析度数、分、秒(这只是基数 60 算术)之外,您可能还需要处理将双精度转换为纬度“北/南”和经度“东/西”的符号。

识别北半球的正纬度和南半球的负纬度是非常标准的。在西半球也很常见,将正经度表示格林威治子午线以西的度数,反之,将负经度表示该子午线以东的度数。然而,对此的首选惯例是相反的,将格林威治子午线以东的度数作为负数。您可能需要咨询您的客户/分析应用程序设计以确定适用于此转换的选择。

另请注意,经度在 ±180 处的不连续性是在转换可能由计算产生的坐标时要小心的一个原因。如果转换不打算处理 180° 子午线处的环绕,那么很可能应该为此类输入抛出异常。当然,无论哪种方式都应该记录设计决策。

当然,在 ±90° 范围之外的纬度是输入错误。

补充: 鉴于解析纬度和经度的上述差异,最好在不同的 ParseLatitude 和 ParseLongitude 例程中处理的问题,我们可以使用通用实用程序进行从双精度到度/分/秒的转换。

我不确定目标语言应该是什么,所以我用普通的 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 和 ParseLongitude 应该管理角度符号到适当地理名称的转换,但我已经包含了一个参数 Sign ,它将允许在转换进行符号检查(尽管如果只调用转换就可以了非负角)。

我使函数 double2DegMinSec 的返回类型为 void。因此,结果将通过其类型为 int 的指针和指向 double 的指针的形式参数返回(在秒 Sec 的情况下,可能有小数部分)。

在 C 中调用转换可能是这样完成的:

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

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

在 C++ 中,我们将通过使用引用调用而不是指针来使调用语法变得有点笨拙。

于 2010-12-22T00:03:08.873 回答
0

只需乘以你就会得到转换错误而不会注意到它,我在地图上映射点时注意到了它。您需要考虑斜率和其他变量,如下所示:

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

我猜你正在使用墨卡托值作为双重表示。要将墨卡托值转换回正确的经度/纬度值,只需使用相反的方法:

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

这对我有用。

于 2011-04-07T11:13:24.610 回答