半正矢公式和文森蒂公式哪个更适合计算距离?

问题描述 投票:0回答:3

半正矢公式和文森蒂公式哪个更适合计算两个纬度/经度点之间的距离?为什么?

这个距离显然是在地球上计算的。 WGS84 vs GCJ02 坐标是否会影响计算或距离(Vincenty 公式考虑了 WGS84 轴)?

例如,在 Android 中,半正矢公式用于 Google Map Utils,但 Vincenty 公式用于

android.Location
对象 (
Location.distanceBetween()
)。

language-agnostic geospatial latitude-longitude trigonometry
3个回答
70
投票

Haversine 和 Vincenty 是两种求解不同问题的算法 问题。 半正矢计算球体上的大圆距离 Vincenty 计算物体表面上的最短(测地线)距离 公转椭球体。 所以你的问题的答案可能会被打破 分为两部分:

  1. 您想计算椭球体上球体的距离吗?
  2. Haversine 或 Vincenty 在计算给定问题时有多准确?

对于地面应用,旋转椭球体是合理的 近似于“平均海平面”;误差为±100m。 这 该椭球体的扁平度很小,大约为 1/300,因此可以 近似为球体(例如,等体积)。

大圆距离与测地距离最多相差 0.5%。 在 一些应用程序,例如,从开普敦到开罗的距离是多少? 这个错误可以忽略。 在其他应用中,例如,确定 海洋边界,它太大了(在 1 的距离内有 5 m) 公里)。 一般来说,使用测地距离更安全。

如果您对旅行距离(乘汽车、乘船或乘飞机)感兴趣, 所采取的道路上有很多限制,而且伟大的 圆或测地距离,测量最短路径的长度 在理想的表面上,是合适的。

关于算法是否准确的问题:

Haversine 可以精确地进行四舍五入,除非分数接近 对映体。 更好的公式在 关于大圆距离的维基百科文章

Vincenty 通常精确到 0.1 毫米左右。 但是如果点是 接近对映体,算法无法收敛,误差为 大得多。 我给出了一个更好的算法来解决测地线问题 在测地线算法中。 另请参阅 关于椭球测地线的维基百科文章

解决测地线问题比解决测地线问题慢 大圆。 但它仍然非常快(每次计算大约 1 μs),所以 这不应该成为选择大圆距离的理由。

附录

Here是实现我的算法的Java包 用于查找测地距离。 与 Vincenty 的方法不同,这是准确的 四舍五入并处处收敛。


26
投票

Haversine 是一种更简单的计算,但它无法提供 Vincenty 提供的高精度。

Vincenty 更准确,但计算量也更大,因此执行速度较慢并增加电池使用量。

与任何“更好”的事物一样,取决于您的特定应用程序。 对于您的应用程序,Vincenty 可能是比 Haversine“更好”的选择,但对于不同的应用程序,Haversine 可能是更好的选择。 您必须查看用例的详细信息,并根据您在那里发现的内容做出决定。


0
投票

让我们比较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}%")
© www.soinside.com 2019 - 2024. All rights reserved.