Vincenty和大圆距离计算之间的区别?


16

Python的geopy软件包具有两种距离测量技术:Great CircleVincenty的公式

>>> from geopy.distance import great_circle
>>> from geopy.distance import vincenty
>>> p1 = (31.8300167,35.0662833) # (lat, lon) - https://goo.gl/maps/TQwDd
>>> p2 = (31.8300000,35.0708167) # (lat, lon) - https://goo.gl/maps/lHrrg
>>> vincenty(p1, p2).meters
429.16765838976664
>>> great_circle(p3, p4).meters
428.4088367903001

有什么区别?首选哪种距离测量?

Answers:


18

根据Wikipedia的说法,Vincenty的公式较慢,但更准确

Thaddeus Vincenty(1975a)开发了Vincenty公式,这是大地测量学中用于计算球体表面两点之间距离的两种相关的迭代方法,它们基于地球图形是扁球形的假设,因此比假设圆球状地球的大圆距等方法更准确。

~0.17%以色列的精确度差为428米。我进行了一次快速的速度测试:

<class 'geopy.distance.vincenty'>       : Total 0:00:04.125913, (0:00:00.000041 per calculation)
<class 'geopy.distance.great_circle'>   : Total 0:00:02.467479, (0:00:00.000024 per calculation)

码:

import datetime
from geopy.distance import great_circle
from geopy.distance import vincenty
p1 = (31.8300167,35.0662833)
p2 = (31.83,35.0708167)

NUM_TESTS = 100000
for strategy in vincenty, great_circle:
    before = datetime.datetime.now()
    for i in range(NUM_TESTS):
        d=strategy(p1, p2).meters
    after = datetime.datetime.now()
    duration = after-before
    print "%-40s: Total %s, (%s per calculation)" % (strategy, duration, duration/NUM_TESTS)

结论: Vincenty的公式与大圆相比是计算时间的两倍,并且在测试点的准确度约为0.17%。

由于计算时间可以忽略不计,因此文森特公式对于每种实际需求都是首选的。

更新:根据whubercffkcffk的答案的深刻见解,我同意应将准确度增益与误差而不是测量进行比较。因此,Vincenty的公式更精确几个数量级,而不是〜0.17%。


3
+1做得好。有关整个地球上的错误的一般分析,请参见gis.stackexchange.com/questions/25494上的线程。
whuber

3
Vincenty所计算的椭圆测地距离比大圆公式精确得多。因此,说Vincenty的准确度增益仅为0.17%会产生误导。(这相当于说双精度算术比使用滑

14

如果您使用的是geopy,那么great_circle和vincenty的距离同样很方便。在这种情况下,您几乎应该始终使用可带来更准确结果的结果,即文森特。正如您所指出的,两个考虑因素是速度和准确性。

Vincenty慢两倍。但是可能在实际应用中,增加的运行时间可以忽略不计。即使您的应用程序要求进行一百万次距离计算,我们只是在谈论几秒钟之间的差异。

对于您使用的点,Vincenty的误差为6μm,大圆距的误差为0.75 m。然后,我会说Vincenty的准确度是120000倍(而不是0.17%的准确度)。对于一般点,大圆距中的误差可能高达0.5%。那么,您可以忍受0.5%的距离误差吗?对于休闲用途(开普敦到开罗的距离是多少?),也许可以。但是,许多GIS应用程序对精度的要求更高。(0.5%是1公里以上的5m。的确确实有所作为。)

几乎所有认真的制图工作都是在参考椭球上进行的,因此有意义的是,也应在椭球上测量距离。也许您今天可以远离长圆距离。但是对于每个新应用程序,您都必须检查是否仍然可以接受。更好的方法是从开始就使用椭圆距离。晚上你会睡得更好。

附录(2017年5月)

回复@ craig-hicks给出的答案。geopy中的vincenty()方法确实存在潜在的致命缺陷:它几乎对映点抛出错误。代码中的文档建议增加迭代次数。但这不是一个通用的解决方案,因为vincenty()使用的迭代方法对于此类问题是不稳定的(每次迭代都使您远离正确的解决方案)。

为什么将问题描述为“潜在致命”?因为在另一个软件库中对距离函数的任何使用都需要能够处理异常。通过返回NaN或大圆距离来处理它可能不令人满意,因为所得的距离函数将不服从三角形不等式,这会妨碍它在例如优势点树中的使用。

情况并不完全暗淡。我的Python包 geographiclib准确计算测地距离而没有任何故障。该geopy拉请求#144,如果它是可用的改变geopy的距离函数使用geographiclib包。不幸的是,自2016年8月以来,此请求一直处于停滞状态。

附录(2018年5月)

geopy 1.13.0现在使用geolib软件包来计算距离。这是一个示例调用(基于原始问题中的示例):

>>> from geopy.distance import great_circle
>>> from geopy.distance import geodesic
>>> p1 = (31.8300167,35.0662833) # (lat, lon) - https://goo.gl/maps/TQwDd
>>> p2 = (31.8300000,35.0708167) # (lat, lon) - https://goo.gl/maps/lHrrg
>>> geodesic(p1, p2).meters
429.1676644986777
>>> great_circle(p1, p2).meters
428.28877358686776

3

我很抱歉在这里发布第二个答案,但是我借此机会回应@ craig-hicks的请求,以提供用于计算测地距离的各种算法的准确性和时序比较。这解释了我对geopy的请求请求144的注释,该注释允许使用在geopy中使用的我的Geodesics算法的两个实现之一,一个是本机python实现 geodesic (geographiclib),另一种使用C语言中实现geodesic(pyproj)

这是一些时序数据。时间以毫秒为单位

method                          dist    dest
geopy great_circle              20.4    17.1
geopy vincenty                  40.3    30.4
geopy geodesic(pyproj)          37.1    31.1
geopy geodesic(geographiclib)  302.9   124.1

这是基于我的测地线测试集的测地线计算的准确性 。误差以微米(1e-6 m)为单位给出

method                        distance destination
geopy vincenty                 205.629  141.945
geopy geodesic(pyproj)           0.007    0.013
geopy geodesic(geographiclib)    0.011    0.010

我包括了hannosche的pull请求#194,它修复了目标函数中的一个错误。如果没有此修复程序,在vincenty的目标计算中的错误是8.98米。

19.2%的测试用例因vincenty.distance而失败(迭代次数= 20)。但是,测试集偏向于会导致这种失败的情况。

对于WGS84椭球上的随机点,可以确保Vincenty算法在1000000次故障中失败16.6次(正确的解决方案是Vincenty方法的不稳定固定点)。

在Vincenty的几何实现和迭代= 20的情况下,失败率为82.8 /1000000。在迭代= 200的情况下,失败率为21.2 / 1000000。

即使这些比率很小,失败也很常见。例如,在一个包含1000个随机点的数据集中(也许想想世界机场),计算整个距离矩阵将平均失败16次(迭代次数= 20)。


2

看来geopy.distance包提供了一个函数“ distance()”,该函数默认为vincenty()。我建议原则上使用distance(),因为它是软件包的建议,以防将来将来与vincenty()背离(不太可能如此)。继续阅读:

该文档说明包含在您指定的vincenty()函数的源代码中:

注意:Vincenty距离的此实现未能收敛某些有效点。在某些情况下,可以通过增加迭代次数来获得结果(iterations关键字参数,在类中提供__init__,默认为20)。最好使用:class .great_circle:,虽然精度稍差一些,但始终会产生结果。

上面带有注释/注释的源代码可以在https://github.com/geopy/geopy/blob/master/geopy/distance.py处找到。 向下滚动到vincenty()的定义

但是,该软件包在校准distance()时使用的默认距离函数是vincenty()函数,这意味着收敛失败不会带来灾难性的后果,并且会返回合理的答案-最重要的是不会生成异常。

更新:如“ cffk”所述,当算法未收敛时,vincenty()函数确实明确抛出ValueError异常-尽管函数描述中未对此进行记录。因此,该文档是错误的。


不,vincenty()方法可以生成异常。人们通常认为这无关紧要,因为它只影响近似对映点之间距离的计算。但是,这样的失败意味着三角形不等式失败,因此无法使用Vincenty距离使用有利点树(例如,您可以高效地确定最近机场的位置)来实现最近邻居搜索。为了解决这个问题,您可以使用此geopy拉取请求github.com/geopy/geopy/pull/144,该请求使用GeographicLib作为距离。
cffk

@cffk-我无法从您的评论或链接中确定,但是我猜“ geopy pull request”可能是一个查找表-是吗?讨论可分为两部分:查找表不可用(下载)和可用的情况。
克雷格·希克斯

@cffk-在无法使用的情况下:首先,该文档存在错误,主要是因为该文档不包含计划的异常的描述(引发ValueError(“ Vincenty公式无法收敛!”)),但是也由于它并没有描述不稳定性是在测量接近对映点时发生的。我建议向Vincenty类添加一个vincenty_noexcpt函数,该函数在内部捕获异常并返回一个很大的圆圈值,并将其设置为默认设置:distance = vincenty_noexcep。
克雷格·希克斯

@cffk-在查找表可用的情况下:我建议进行大量测试和计时,因为查找方法通常不在高速缓存之外,因此非常耗时。将vincenty方法替换为默认的“拉”方法可能意味着任何将“拉”包下载到python目录中的人都会将对vincenty的所有现有调用更改为对pull的调用-如果用户真的只是想仔细明确地尝试“拉”方法。
克雷格·希克斯

@ craig-hicks-不,“拉取请求”替代了更好的算法(对我来说!)来测量距离,请参阅doi.org/10.1007/s00190-012-0578-z这比Vincenty更准确,总是返回结果,大约需要相同的时间。我不是geopy的维护者,并且自去年8月以来,该请求一直处于休眠状态。如果我有德鲁特,则将其替换为geopy(并且vincenty()将调用新算法,而不是Vincenty的算法),这将结束讨论。
cffk

1

无论是使用vincenty还是hasrsine或余弦的球律,明智的做法是了解您计划使用的代码的任何潜在问题,需要注意和缓解的问题以及如何处理vincenty,haversine,sloc问题当人们意识到每个人的潜伏问题/边缘情况时,情况可能会有所不同,这可能会或可能不会广为人知。经验丰富的程序员知道这一点。新手可能不会。在某些情况下,如果论坛中的摘要片段执行了某些意外操作,我希望可以避免其中的一些挫败感。如果认真使用其中任何一种的某些版本,例如vincenty,haversine,sloc,SE,SO,Reddit,Quora等,则可能在解决方案的某些初始编码中提供了有限的帮助,但这并不意味着他们的解决方案或公认的“答案”没有问题。如果一个项目足够重要,则应该进行适当合理数量的研究。阅读手册,阅读文档,如果存在对该代码的代码审查,请阅读该手册。复制并粘贴已被投票一百次或更多次的摘录或要点并不意味着其安全性是全面且有保证的。

cffk提出的有趣的答案提出了要注意在打包的解决方案中潜伏着可能产生异常或其他困难的边缘情况的意义。该职位提出的具体要求超出了我目前的时间预算,但我认为某些包中确实存在潜伏的问题,其中包括至少一项文森特实施,至少有人建议对此进行改进一种方法或另一种方法,以最大程度地减少或消除遇到这些困难的风险。我不会在有关vincenty的话题上做进一步的介绍(对此太不了解了),但是将转而将其改为hasrsine,至少部分是关于OP的话题。

广受欢迎的Haversine公式,无论是python还是其他语言,因为它很可能会在当今大多数所有intel和intel-like系统以及ARM处理器,powerPC等上使用IEEE 754浮点规范。由于浮点近似和舍入,在极近或在180度弧距离处也很容易遇到罕见但真实且可重复的异常错误,即对极点。有些新手可能尚未被这种情况咬伤。因为此fp规范是近似值和四舍五入,所以这并不意味着任何在fp64上调用的代码都可能导致异常错误。但是一些代码 一些公式可能没有那么明显的边缘情况,在这些情况下,IEEE 754 fp64的近似值和舍入可能导致某个值稍微偏离数学方法的范围,而该数学方法可以完美地评估该值。一个例子... sqrt()。如果负数进入sqrt(),例如sqrt(-0.00000000000000000122739),则将出现异常错误。在haversine公式中,它走向求解的方式在atan2()中有两种sqrt()方法。的经过计算然后在sqrt()中使用的a,可以在地球上的对映点稍微偏离0.0或1.0以下,由于fp64近似和四舍五入而非常轻微,很少但可重复。在这种情况下,一致的可靠可重复性使它成为例外风险,这是保护,缓解而不是孤立的随机fl幸的边缘情况。这是haversine的简短python3片段示例,没有必要的保护:

import math as m

a = m.sin(dlat / 2)**2 + m.cos(lat1) * m.cos(lat2) * m.sin(dlon / 2)**2
c = 2 * m.atan2(m.sqrt(a), m.sqrt(1 - a))
distance = Radius * c

在公式的第一行中非常接近或在对映点处,a可能会偏离负值,很少出现,但对于相同的纬度坐标重复出现。为了保护/纠正这些罕见的事件,一个可以简单地添加,后一个计算,所看到如下:

import math as m

note = ''

a = m.sin(dlat / 2)**2 + m.cos(lat1) * m.cos(lat2) * m.sin(dlon / 2)**2
if a < 0.0: a = 0.0 ; note = '*'
if a > 1.0: a = 1.0 ; note = '**'
c = 2 * m.atan2(m.sqrt(a), m.sqrt(1 - a))
distance = Radius * c

# note = '*'  # a went below 0.0 and was normalized back to 0.0
# note = '**' # a went above 1.0 and was normalized back to max of 1.0

当然,我并没有在此处显示整个功能,而是发布了一个简短的摘要。但是通过测试a并在必要时对其进行规范化,此示例显示了对sqrt()的保护,同时还省去了将整个过程都尝试一遍的需要。note =“ up up top”是为了防止字节码阶段在分配了值之前抗议使用了该注释,如果该注释随函数结果一起返回。

通过简单的更改,即添加两个a测试,sqrt()函数将很高兴,并且代码现在具有可以返回到调用代码的附加注释,以警告结果已被稍微规范化以及原因。有些人可能在乎,有些人可能不在乎,但是它在那里,防止了“否则”可能发生的异常错误。除非明确编写,try try块可能会捕获该异常,但不能解决该异常。它似乎更容易在后码校正线(S)一个计算行。然后,彻底擦洗的输入应该完全不需要尝试,除非此处阻塞。

总之,如果采用半正矢,显式编码,而不是用包或库,无论你选择的语言,这将是一个好主意,测试和正常化一个为了回0.0要紧的范围<= A <= 1.0用c计算保护下一行。但是大多数haversine代码段没有显示它,也没有提及风险。

经验:在全球范围内以0.001度为增量进行全面测试的过程中,我已经用latlon组合填充了一个硬盘驱动器,该组合导致一个异常(可靠的一致的可重复异常),并且在一个月内同时对CPU冷却的可靠性进行了测试。粉丝,还有我的耐心。是的,自那以后,我删除了大多数这些日志,因为它们的目的主要是为了证明这一点(如果允许使用双关语)。但是我有一些较短的“问题纬度值”日志,用于测试目的。

准确性:a和整个havesine结果会通过将其归一化到域中而损失一些准确性吗?引入的fp64近似值和舍入不多,也许不多,这引起了域外的轻微漂移。如果您已经发现Haversine已经可以接受vincenty了-更简单,更快,更易于自定义,排除故障和维护,那么Haversine可能是您的项目的理想解决方案。

从地球上的某个位置看,我已经在高架投影的天空层上使用了hasversine来测量天空中的对象之间的角距离,将方位角和alt映射到与latlat lonlon等价的坐标类似的坐标,根本没有要考虑的椭球体,因为当从地球表面的某个位置测量两个物体之间的角距离外观时,投影理论天空球是一个理想的球。非常适合我的需求。因此,在某些应用中(正好在我的目的之内),haversine仍然非常有用且非常准确……但是,如果您确实使用它,无论是在地球上进行GIS还是导航,还是在天体观测和测量中,都应加以保护它通过测试径点或非常接近径点的情况下,并在需要时将其推回其需要的域。

不受保护的Haversine遍及整个Internet,并且我只看到一个老的usenet帖子显示了一些保护,我认为是JPL的某个人提供的,并且可能是1985年前的IEEE 754浮点规范。另外两页提到了对点附近的可能问题,但没有描述这些问题,也没有描述如何解决这些问题。因此,对于像我这样的新手来说,可能会感到担忧,他们可能并不总是对良好实践有足够的了解,无法进一步研究和测试他们复制并粘贴到信任项目中的某些代码的边缘情况。与发布的未受保护和未经讨论的版本相比,cffk令人着迷的帖子令人耳目一新,因为它公开发布了这类问题,这些问题很少提及,很少公开编码以保护摘要,并且很少采用这种方式进行讨论。

截至20190923,由于计算设备中存在浮点问题,haversine公式的wiki页面确实确实提到了对映点可能存在的问题...令人鼓舞...

https://zh.wikipedia.org/wiki/Haversine_formula

(因为该Wiki页面目前没有我直接链接到的部分的html锚,因此,在该页面加载后,请在该浏览器页面上搜索“何时使用这些公式”,可以更正式地看到有关正反点的长石问题。)

这个站点也对此进行了非常简短的提及:

https://www.movable-type.co.uk/scripts/latlong.html

如果在该页面上找到“包括防止舍入错误的保护措施”,那么就会有...

如果atan2不可用,则可以从2⋅asin(min(1,√a))计算c(包括防止舍入误差)。

现在,有一种罕见的情况,其中提到了舍入错误,并且针对asin()版本显示了保护,而对于atan2()版本则没有提及或显示。但是至少提到了舍入错误的风险。

imho,任何使用正己烷的24/7/365应用程序,都需要在反点附近提供这种保护,这是重要且简单的细节。

我不知道哪些hasrsine软件包提供或不提供这种保护,但是如果您不熟悉这一切,并且打算使用流行发行的“代码段”版本,现在您知道它需要保护,并且这种保护非常容易实现,也就是说,如果您不使用vincenty,并且没有使用打包的强效剂,而无法轻松访问修改包的代码的话。

IOW,无论使用vincenty,haversine还是sloc,都应该了解代码中的任何问题,需要注意和缓解的问题,以及随着人们意识到每个人对vincenty,haversine,sloc问题的处理方式会有所不同潜伏的问题/边缘情况,可能会或可能不会广为人知。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.