Answers:
我最近在WGS-84数据上使用“ Haversine公式”进行了类似的操作,它是“ Haversines法”的派生词,具有非常令人满意的结果。
是的,WGS-84假定地球是一个椭球,但是我相信使用“ Haversine公式”之类的方法只能得到大约0.5%的平均误差,这在您的情况下可能是可接受的误差。除非您谈论的是几英尺的距离,否则理论上总是会有一些误差的,即使如此,理论上讲地球也会弯曲……如果您需要更严格地与WGS-84兼容的方法,请查看“ Vincenty公式”。
我了解starblue的来源,但是好的软件工程通常是要权衡取舍的,因此,这一切都取决于您执行工作所需的准确性。例如,从“曼哈顿距离公式”计算得出的结果与从“距离公式”得出的结果相比,在某些情况下可能会更好,因为它在计算上更便宜。认为“哪一点最接近?” 不需要精确距离测量的场景。
关于“ Haversine公式”,它易于实现并且很好,因为它使用的是“球面三角法”,而不是基于二维三角法的“余弦法”,因此您可以获得很好的精度平衡过于复杂。
一位名叫克里斯·韦尼斯(Chris Veness)的先生们在http://www.movable-type.co.uk/scripts/latlong.html上有一个不错的网站,它解释了您感兴趣的一些概念并演示了各种程序实现。这也应该回答您的x / y转换问题。
这是我找到的答案:
为了使定义完整,在笛卡尔坐标系中:
转换为:
x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
其中R是地球的近似半径(例如6371 km)。
如果三角函数期望弧度(它们可能会这样做),则需要先将经度和纬度转换为弧度。显然,您需要用十进制表示,而不是度/分钟/秒(请参见此处有关转换的信息)。
反向转换的公式:
lat = asin(z / R)
lon = atan2(y, x)
阿辛当然是反正弦的。在Wikipedia中阅读有关atan2的信息。不要忘记将弧度转换回度。
此页面为此提供了C#代码(请注意,它与公式大不相同),并提供了一些解释和很好的图表说明了为什么这样做是正确的,
转换GPS(WGS84)
为笛卡尔坐标的
理论https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates
以下是我正在使用的:
我附上了我写的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
高度必须被转换到高度h
高于WGS 84 ellipsoid
通过使用位势模型EGM96
(勒蒙纳等人,1998)。
这是通过以15弧分的空间分辨率内插大地水准面高度文件的网格来完成的。
或者,如果您有某个级别的专业人员 GPS
具有“海拔” H
(msl,高于平均海平面)和UNDULATION
,则从内部表中选择的基准数据的geoid
和之间的关系。你可以得到ellipsoid (m)
h = H(msl) + undulation
通过直角坐标到XYZ:
x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
在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
如果您想获取基于椭球而不是球体的坐标,请查看http://en.wikipedia.org/wiki/Geodetic_system#From_geodetic_to_ECEF-它提供了转换所需的公式以及WGS84常数。
此处的公式还考虑了相对于参考椭球面的高度(如果要从GPS设备获取高度数据,则很有用)。
为什么要实施已经实施并经过测试证明的东西?
C#具有一个NetTopologySuite,它是JTS拓扑套件的.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
您可以在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;
}