从经度\纬度转换为笛卡尔坐标

问题描述 投票:92回答:9

我有一些以地球为中心的坐标点,以纬度和经度(WGS-84)给出。

如何将它们转换为笛卡尔坐标(x,y,z),原点位于地球的中心?

mapping geometry geospatial
9个回答
41
投票

我最近使用WGS-84数据的“Haversine Formula”做了类似的事情,这是“Haversines法则”的衍生物,结果非常令人满意。

是的,WGS-84假设地球是一个椭圆体,但我相信你只能使用类似“Haversine Formula”的方法得到大约0.5%的平均误差,这可能是你的情况下可接受的误差量。你总会有一些误差,除非你说的是几英尺的距离,甚至理论上地球的曲率......如果你需要更严格的WGS-84兼容方法,请查看“Vincenty公式”。

我知道starblue的来源,但良好的软件工程往往是权衡取舍,所以这一切都取决于你所做的准确性。例如,从“曼哈顿距离公式”计算的结果与“距离公式”的结果相比,对于某些情况可能更好,因为它在计算上更便宜。想想“哪一点最接近?”您不需要精确距离测量的场景。

关于,“Haversine公式”很容易实现并且很好,因为它使用“球面三角法”而不是基于二维三角法的“余弦定律”方法,因此您可以获得精确的平衡过于复杂。

一位名叫Chris Veness的绅士在http://www.movable-type.co.uk/scripts/latlong.html有一个很棒的网站,它解释了一些你感兴趣的概念并演示了各种程序化实现;这也应该回答你的x / y转换问题。


116
投票

这是我找到的答案:

只是为了使定义完整,在笛卡尔坐标系中:

  • x轴经过长,纬度(0,0),因此经度0与赤道相遇;
  • y轴经过(0,90);
  • 并且z轴穿过两极。

转换是:

x = R * cos(lat) * cos(lon)

y = R * cos(lat) * sin(lon)

z = R *sin(lat)

其中R是the approximate radius of earth(例如6371KM)。

如果您的三角函数需要弧度(他们可能会这样做),您需要先将经度和纬度转换为弧度。你显然需要一个十进制表示,而不是degrees \ minutes \ seconds(有关转换的信息,请参阅e.g. here)。

反向转换公式:

   lat = asin(z / R)
   lon = atan2(y, x)

asin当然是正弦曲线。 read about atan2 in wikipedia。不要忘记从弧度转换回度数。

This page为此提供了c#代码(注意它与公式有很大不同),还有一些解释和很好的图表,为什么这是正确的,


6
投票

GPS(WGS84)转换为笛卡尔坐标https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates的理论

以下是我正在使用的内容:

  • GPS(WGS84)和笛卡尔坐标的经度是相同的。
  • 纬度需要通过WGS 84椭球参数转换半长轴为6378137米,并且
  • 扁平化的倒数是298.257223563。

我附上了我写的VB代码:

Imports System.Math

'Input GPSLatitude is WGS84 Latitude,h is altitude above the WGS 84 ellipsoid

Public Function GetSphericalLatitude(ByVal GPSLatitude As Double, ByVal h As Double) As Double

        Dim A As Double = 6378137 'semi-major axis 
        Dim f As Double = 1 / 298.257223563  '1/f Reciprocal of flattening
        Dim e2 As Double = f * (2 - f)
        Dim Rc As Double = A / (Sqrt(1 - e2 * (Sin(GPSLatitude * PI / 180) ^ 2)))
        Dim p As Double = (Rc + h) * Cos(GPSLatitude * PI / 180)
        Dim z As Double = (Rc * (1 - e2) + h) * Sin(GPSLatitude * PI / 180)
        Dim r As Double = Sqrt(p ^ 2 + z ^ 2)
        Dim SphericalLatitude As Double =  Asin(z / r) * 180 / PI
        Return SphericalLatitude
End Function

请注意,h高于WGS 84 ellipsoid

通常GPS将给我们H高于MSL。通过使用位势模型MSL(Lemoine等,1998),h高度必须转换为WGS 84 ellipsoid上方的高度EGM96。 这是通过插入大地水准面高度文件的网格以15弧分的空间分辨率来完成的。

或者,如果你有一些级别的专业GPS有海拔高度H(msl,高于平均海平面高度)和UNDULATIONgeoidellipsoid (m)之间的关系从内部表格中选择的基准输出。你可以得到h = H(msl) + undulation

通过笛卡尔坐标到XYZ:

x = R * cos(lat) * cos(lon)

y = R * cos(lat) * sin(lon)

z = R *sin(lat)

5
投票

proj.4软件提供了一个可以进行转换的命令行程序,例如

LAT=40
LON=-110
echo $LON $LAT | cs2cs +proj=latlong +datum=WGS84 +to +proj=geocent +datum=WGS84

它还提供了C API。特别是,函数pj_geodetic_to_geocentric将在不必首先设置投影对象的情况下进行转换。


4
投票

为什么要实施已经实施并经过测试验证的东西?

例如,C#有NetTopologySuite,它是JTS拓扑套件的.NET端口。

具体来说,您的计算存在严重缺陷。地球不是一个完美的球体,地球半径的近似可能不会削减它以进行精确测量。

如果在某些情况下使用自制函数是可以接受的,那么GIS就是一个很好的例子,在这个领域中,使用可靠的,经过测试验证的库是非常优选的。


3
投票

如果您关心的是基于椭球而不是球体来获取坐标,请查看http://en.wikipedia.org/wiki/Geodetic_system#From_geodetic_to_ECEF - 它给出了转换所需的公式以及WGS84常量。

那里的公式还考虑了相对于参考椭球表面的高度(如果您从GPS设备获取高度数据,则非常有用)。


2
投票

在python3.x中,它可以使用:

# Converting lat/long to cartesian
import numpy as np

def get_cartesian(lat=None,lon=None):
    lat, lon = np.deg2rad(lat), np.deg2rad(lon)
    R = 6371 # radius of the earth
    x = R * np.cos(lat) * np.cos(lon)
    y = R * np.cos(lat) * np.sin(lon)
    z = R *np.sin(lat)
    return x,y,z

1
投票
Coordinate[] coordinates = new Coordinate[3];
coordinates[0] = new Coordinate(102, 26);
coordinates[1] = new Coordinate(103, 25.12);
coordinates[2] = new Coordinate(104, 16.11);
CoordinateSequence coordinateSequence = new CoordinateArraySequence(coordinates);

Geometry geo = new LineString(coordinateSequence, geometryFactory);

CoordinateReferenceSystem wgs84 = DefaultGeographicCRS.WGS84;
CoordinateReferenceSystem cartesinaCrs = DefaultGeocentricCRS.CARTESIAN;

MathTransform mathTransform = CRS.findMathTransform(wgs84, cartesinaCrs, true);

Geometry geo1 = JTS.transform(geo, mathTransform);

-1
投票

你可以在Java上这样做。

public List<Double> convertGpsToECEF(double lat, double longi, float alt) {

    double a=6378.1;
    double b=6356.8;
    double N;
    double e= 1-(Math.pow(b, 2)/Math.pow(a, 2));
    N= a/(Math.sqrt(1.0-(e*Math.pow(Math.sin(Math.toRadians(lat)), 2))));
    double cosLatRad=Math.cos(Math.toRadians(lat));
    double cosLongiRad=Math.cos(Math.toRadians(longi));
    double sinLatRad=Math.sin(Math.toRadians(lat));
    double sinLongiRad=Math.sin(Math.toRadians(longi));
    double x =(N+0.001*alt)*cosLatRad*cosLongiRad;
    double y =(N+0.001*alt)*cosLatRad*sinLongiRad;
    double z =((Math.pow(b, 2)/Math.pow(a, 2))*N+0.001*alt)*sinLatRad;

    List<Double> ecef= new ArrayList<>();
    ecef.add(x);
    ecef.add(y);
    ecef.add(z);

    return ecef;


}
© www.soinside.com 2019 - 2024. All rights reserved.