半正矢公式和文森蒂公式哪个更适合计算两个纬度/经度点之间的距离?为什么?
这个距离显然是在地球上计算的。 WGS84 vs GCJ02 坐标是否会影响计算或距离(Vincenty 公式考虑了 WGS84 轴)?
例如,在 Android 中,半正矢公式用于 Google Map Utils,但 Vincenty 公式用于
android.Location
对象 (Location.distanceBetween()
)。
Haversine 和 Vincenty 是两种求解不同问题的算法 问题。 半正矢计算球体上的大圆距离 Vincenty 计算物体表面上的最短(测地线)距离 公转椭球体。 所以你的问题的答案可能会被打破 分为两部分:
对于地面应用,旋转椭球体是合理的 近似于“平均海平面”;误差为±100m。 这 该椭球体的扁平度很小,大约为 1/300,因此可以 近似为球体(例如,等体积)。
大圆距离与测地距离最多相差 0.5%。 在 一些应用程序,例如,从开普敦到开罗的距离是多少? 这个错误可以忽略。 在其他应用中,例如,确定 海洋边界,它太大了(在 1 的距离内有 5 m) 公里)。 一般来说,使用测地距离更安全。
如果您对旅行距离(乘汽车、乘船或乘飞机)感兴趣, 所采取的道路上有很多限制,而且伟大的 圆或测地距离,测量最短路径的长度 在理想的表面上,是合适的。
关于算法是否准确的问题:
Haversine 可以精确地进行四舍五入,除非分数接近 对映体。 更好的公式在 关于大圆距离的维基百科文章。
Vincenty 通常精确到 0.1 毫米左右。 但是如果点是 接近对映体,算法无法收敛,误差为 大得多。 我给出了一个更好的算法来解决测地线问题 在测地线算法中。 另请参阅 关于椭球测地线的维基百科文章。
解决测地线问题比解决测地线问题慢 大圆。 但它仍然非常快(每次计算大约 1 μs),所以 这不应该成为选择大圆距离的理由。
附录
Here是实现我的算法的Java包 用于查找测地距离。 与 Vincenty 的方法不同,这是准确的 四舍五入并处处收敛。
让我们比较Haversine 与Karney(更快、更稳定的Vincenty 重新表述)距离计算。如果我们假设地球的椭球模型是准确的,并且忽略距离上的 GPS 高程,那么卡尼的计算将是准确的。半正矢距离会有误差,因为它们假设地球是球形而不是椭球体。
为了最小化误差,半正弦计算应使用与您的 GPS 兴趣点附近的椭球体上的径向距离相匹配的地球球半径。对于地球上随机分布的 GPS 点,极地半径和赤道半径长度中间的半径将最大限度地减少误差。对于 WGS84 参考椭球体,这是
6367444.65712259
米。
使用这个半径,我们可以测量随机 GPS 点的半正弦距离并与卡尼进行比较。最大相对绝对误差是常数~0.5052%。对于 1 公里的距离,这意味着半正矢距离最多会偏离 5.052 米。
半正弦计算更加简单且计算速度更快,因此如果您的应用可以接受 0.5052% 的相对误差,则应该首选它。
这里有一些 Python 代码,您可以使用它们来测量不同半正弦半径或不同距离的误差:
""" Compare haversine vs karney distances """
import math
from random import random
from geographiclib_cython import Geodesic
from geographiclib.constants import Constants
# distance in meters
karney_distance = 100000
def lerp(t, a, b):
if t < 0.5:
return a+(b-a)*t
return b-(b-a)*(1-t)
equatorial_radius = Constants.WGS84_a
polar_radius = Constants.WGS84_a*(1-Constants.WGS84_f)
radius = lerp(0.5, equatorial_radius, polar_radius)
print(f"Haversine radius: {radius}")
def haversine(A, B):
A = tuple(map(math.radians, A))
B = tuple(map(math.radians, B))
dlat = A[0] - B[0]
dlon = A[1] - B[1]
a = math.sin(dlat/2)**2 + math.cos(A[0]) * math.cos(B[0]) * math.sin(dlon/2)**2
c = 2 * math.asin(math.sqrt(a))
return c * radius
max_error = 0
for sample in range(100000):
A = (
lerp(random(), -90.0, 90.0),
lerp(random(), -180.0, 180.0)
)
azimuth = lerp(random(), 0.0, 360.0)
res = Geodesic.WGS84.Direct(*A, azimuth, karney_distance)
B = (
res["lat2"],
res["lon2"]
)
haversine_distance = haversine(A, B)
err = abs(haversine_distance - karney_distance)
if err > max_error:
max_error = err
# haversine formula error in meters
print(f"Maximum relative absolute error: {max_error/karney_distance*100:.04f}%")