我有一些以纬度和经度(WGS-84)表示的以地球为中心的坐标点。
如何将它们转换为以地球中心作为原点的笛卡尔坐标系(x,y,z)?
我有一些以纬度和经度(WGS-84)表示的以地球为中心的坐标点。
如何将它们转换为以地球中心作为原点的笛卡尔坐标系(x,y,z)?
x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
其中 R 是地球的近似半径(例如 6371 公里)。
如果你的三角函数期望弧度作为输入(很可能是这样),你需要先将经度和纬度转换为弧度。你显然需要一个十进制表示,而不是度\分\秒表示(参见例如这里关于转换)。
反向转换的公式:
lat = asin(z / R)
lon = atan2(y, x)
我最近使用WGS-84数据和“Haversine公式”做了类似的事情,这是“Haversines定理”的一个衍生物,结果非常令人满意。
是的,WGS-84假设地球是一个椭球体,但我认为使用“Haversine公式”这样的方法平均误差只有大约0.5%,在你的情况下可能是可接受的误差范围。除非你要测量几英尺的距离,否则总会存在一些误差,即使在那种情况下理论上也有地球曲率... 如果你需要更严格符合WGS-84标准的方法,请查看“Vincenty公式”。
我理解starblue的观点,但优秀的软件工程通常涉及权衡,因此它完全取决于你所需的精度。例如,“曼哈顿距离公式”和“距离公式”计算出来的结果在某些情况下可以更好,因为计算成本更低。想象一下需要“哪个点最近?”这种情况,在这种情况下你不需要精确的距离测量。
关于“Haversine公式”,它易于实现,并且很好,因为它使用了“球面三角法”,而不是基于二维三角法的“余弦定理”方法,因此你可以在精度和复杂性之间获得一个很好的平衡。
一位名叫Chris Veness的绅士有一个很棒的网站,解释了你感兴趣的一些概念,并演示了各种编程实现方式;这也应该回答了你的x/y转换问题。
在Python 3.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
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椭球体
的高度。GPS
会给出相对于MSL
高度的H
。
必须使用地势势能模型EGM96
(Lemoine et al, 1998)将MSL
高度转换为相对于WGS 84椭球体
的高度h
。GPS
,具有高度H
(msl,相对于平均海平面的高度)和UNDULATION
,则可以通过内部表格中所选数据输出的大地水准面
和椭球体(m)
之间的关系来获得h = H(msl) + undulation
使用笛卡尔坐标转换为XYZ坐标:x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
proj.4 软件提供了一个可以进行转换的命令行程序,例如:
LAT=40
LON=-110
echo $LON $LAT | cs2cs +proj=latlong +datum=WGS84 +to +proj=geocent +datum=WGS84
#include <math.h>
#include <proj.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
PJ* P = proj_create_crs_to_crs(PJ_DEFAULT_CTX,
"+proj=latlong +datum=WGS84",
"+proj=geocent +datum=WGS84", NULL);
if (P == NULL) {
return 1;
}
PJ_COORD c;
c.lpzt.lam = -110.0; /* Longitude in degrees */
c.lpzt.phi = 40.0; /* Latitude in degrees */
c.lpzt.z = 0.0; /* Height in meters */
c.lpzt.t = HUGE_VAL; /* Time (not used here) */
PJ_COORD c_out = proj_trans(P, PJ_FWD, c);
printf("X = %.17g m\n", c_out.xyzt.x);
printf("Y = %.17g m\n", c_out.xyzt.y);
printf("Z = %.17g m\n", c_out.xyzt.z);
return 0;
}
X = -1673404.5546274509 m
Y = -4597641.2274514409 m
Z = 4077985.5722003761 m
from pyproj import Transformer
lon, lat, hgt = -110., 40., 0.
llh_to_xyz = Transformer.from_crs("+proj=latlong +datum=WGS84", "+proj=geocent +datum=WGS84")
print(llh_to_xyz.transform(lon, lat, hgt))
pj_geodetic_to_geocentric()
不再在API中。 - Chris Gnampj_geodetic_to_geocentric
在2019年随着PROJ 6版本的发布被弃用了。我已经更新了答案,提供了一个适用于PROJ 9.2.0版本的C示例。 - Brian Hawkinspj_geodetic_to_geocentric
在2019年随着PROJ 6版本的发布被弃用了。我已经更新了答案,提供了一个适用于PROJ 9.2.0版本的C示例。 - undefined我在Python中编写了一个函数,考虑到地球不是一个完美的球体。参考资料在注释中:
# this function converts latitude,longitude and height above sea level
# to earthcentered xyx coordinates in wgs84, lat and lon in decimal degrees
# e.g. 52.724156(West and South are negative), heigth in meters
# for algoritm see https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates
# for values of a and b see https://en.wikipedia.org/wiki/Earth_radius#Radius_of_curvature
from math import *
def latlonhtoxyzwgs84(lat,lon,h):
a=6378137.0 #radius a of earth in meters cfr WGS84
b=6356752.3 #radius b of earth in meters cfr WGS84
e2=1-(b**2/a**2)
latr=lat/90*0.5*pi #latitude in radians
lonr=lon/180*pi #longituede in radians
Nphi=a/sqrt(1-e2*sin(latr)**2)
x=(Nphi+h)*cos(latr)*cos(lonr)
y=(Nphi+h)*cos(latr)*sin(lonr)
z=(b**2/a**2*Nphi+h)*sin(latr)
return([x,y,z])
在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;
}
为什么要实现已经实现和经过测试的东西呢?
比如说,C#有NetTopologySuite,它是JTS Topology Suite的.NET端口。
具体来说,你的计算中存在严重缺陷。地球并不是一个完美的球体,对地球半径的近似可能无法满足精确测量的要求。
如果在某些情况下使用自制函数是可以接受的,那么GIS就是一个很好的例子,这个领域更倾向于使用可靠、经过测试的库。
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);
java.lang.IllegalArgumentException: dimension must be <= 3
。 - oschrenk