ChatGPT解决这个技术问题 Extra ChatGPT

如何快速估计两个(纬度、经度)点之间的距离?

我希望能够估计两个(纬度,经度)点之间的距离。我想低于,因为这将用于 A* 图形搜索,我希望它很快。这些点之间的距离最多为 800 公里。

我们应该推断这些点位于一个球体上吗?
是的,在地球上,但速度。 AFAIK 复杂的数学运算速度不够快。
我建议你在断定它不够快之前先测量一下。
有时可能对实现和算法有足够的了解,甚至在基准测试之前就知道性能还不够好。例如,harsine 距离方法不适用的一种情况是,当尝试在接近度上匹配大型数据集时,因为在大多数查询引擎中,haversine 算法不允许任何谓词下推或分区匹配。我们发现,在 250k 记录数据集上,利用近似距离和下推来生成笛卡尔聚类基础花费了约 1/50 的时间。接受的答案需要一周多的时间才能在这里运行。

R
RoyaumeIX

Haversine Formula in Python (Bearing and Distance between two GPS points) 的答案提供了可以回答您的问题的 Python 实现。

使用下面的实现,我在一台较旧的笔记本电脑上在不到 1 秒的时间内执行了 100,000 次迭代。我认为就您的目的而言,这应该足够了。但是,您应该在优化性能之前分析任何内容。

from math import radians, cos, sin, asin, sqrt
def haversine(lon1, lat1, lon2, lat2):
    """
    Calculate the great circle distance between two points 
    on the earth (specified in decimal degrees)
    """
    # convert decimal degrees to radians 
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
    # haversine formula 
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 
    # Radius of earth in kilometers is 6371
    km = 6371* c
    return km

低估 haversine(lat1, long1, lat2, long2) * 0.90 或任何你想要的因素。我看不出在您的低估中引入错误有什么用。


所以,这不是“快速”方法,而是“标准”方法,对吗? (无论如何,在 Excel 中进行比较时,比我同样准确的方法要慢)
@ashleedawg - 假设地球是一个球体,Haversine 公式是一种估计两点之间距离的特殊方法。如果您想探索其他方法,我邀请您查看有关 Haversine 公式的维基百科页面。 Haversine 很常见,但没有“标准”公式。如果您的“同样准确的方法”很好,为什么不在这里发布它作为答案呢?
T
TreyA

由于距离相对较小,您可以使用等距矩形距离近似。这种近似比使用 Haversine 公式更快。因此,要获得从参考点 (lat1/lon1) 到您正在测试的点 (lat2/lon2) 的距离,请使用以下公式。重要提示:您需要将所有纬度/经度点转换为弧度:

R = 6371  // radius of the earth in km
x = (lon2 - lon1) * cos( 0.5*(lat2+lat1) )
y = lat2 - lat1
d = R * sqrt( x*x + y*y )

由于“R”以公里为单位,因此距离“d”将以公里为单位。

参考:http://www.movable-type.co.uk/scripts/latlong.html


请注意,等距矩形近似值会过冲,而不是下冲。您可以使用小角度近似 sin^2(dlon) ~ dlon^2sin^2(dlat) ~ dlat^2cos(dlat) ~ 1 从 Haversine 公式推导出它,其中 dlon=lon2-lon1dlat=lat2-lat1。所有近似值都大于等于精确版本,因此近似距离将大于精确距离。
我可以争辩说,Haversine(如果这就是你所说的“精确”的话)也是一个近似值,因为地球不是一个真正的球体。我们在说什么数量级的过冲?几英寸超调?脚?
当然,这是正确的。 OP要求下冲。但是对于某些应用,超调甚至是近似的期望属性,例如用于预先选择给定距离内的相邻点。所以关键是它过冲,而不是多少。
是的,但是如果它仅超出英寸,从技术上讲你是正确的,但实际上谁在乎呢? OP也在寻找速度。您可以自由调整地球的半径以适应您的低/高射门喜好。
R
Raymond Hettinger

速度的一种想法是将经纬度坐标转换为 3D (x,y,z) 坐标。预处理点后,使用点之间的欧几里得距离作为实际距离的快速计算下冲。


但是将球体投影到平面会引入伪影,并且在 800 公里内它们会很明显。
@jjmontes 不涉及投影。这是从球坐标到笛卡尔坐标的直接转换。这些点不会在空间中移动。
哦,我现在明白了。好的!并且会以不错的属性下冲。
D
Danylo Zherebetskyy

如果点之间的距离相对较小(米到几公里的范围),那么快速方法之一可能是

from math import cos, sqrt
def qick_distance(Lat1, Long1, Lat2, Long2):
    x = Lat2 - Lat1
    y = (Long2 - Long1) * cos((Lat2 + Lat1)*0.00872664626)  
    return 111.319 * sqrt(x*x + y*y)

Lat、Long 以弧度为单位,距离以 km 为单位。

Haversine 距离的偏差约为 1%,而速度增益则超过 ~10 倍。

0.00872664626 = 0.5 * pi/180,

111.319 - 是对应于赤道 1 度的距离,您可以将其替换为中值,如此处 https://www.cartographyunchained.com/cgsta1/ 或用简单的查找表替换。


有人可以澄清如何获得这个神奇的 111.138 号码吗?赤道1度的距离。我如何获得其他地方的适当价值?找到这个,也许有帮助... blog.mapbox.com/…
@LiamMitchell 感谢您注意到错字,我已更正赤道距离:赤道半径 (km)= 6378.137 对应于 nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html,因此 2*pi*R / 360 = 111.319491 km
A
Aaron D

为了获得最大速度,您可以创建类似 rainbow table 的坐标距离。听起来您已经知道您正在使用的区域,因此似乎预先计算它们可能是可行的。然后,您可以加载最近的组合并使用它。

例如,在美国大陆,经度跨度为 55 度,纬度为 20,即 1100 个整数点。所有可能组合之间的距离是 handshake problem,由 (n-1)(n)/2 或大约 600k 个组合回答。存储和检索这似乎很可行。如果您提供有关您的要求的更多信息,我可能会更具体。


G
Grayrigel

您可以使用 scipy 空间距离类中的 cdist

例如:

from scipy.spatial.distance import cdist 
df1_latlon = df1[['lat','lon']]
df2_latlon = df2[['lat', 'lon']]
distanceCalc = cdist(df1_latlon, df2_latlon, metric=haversine)

据我在 cdist 文档 (docs.scipy.org/doc/scipy/reference/generated/...) 中看到的,没有定义半正弦度量。运行它还通过“未知距离度量:haversine”错误确认。
正确的。刚刚交叉检查。只有以下内容可用:布雷柯蒂斯、堪培拉、切比雪夫、城市街区、相关性、余弦、欧几里得、詹森香农、马哈拉诺比斯、明可夫斯基、苏几里得、sqeuclidean。 github.com/scipy/scipy/blob/master/scipy/spatial/distance.py
相反,您可以使用它来查找半正弦距离:github.com/mapado/…
C
Chiefir

要计算 2 个点之间的半正弦距离,您可以简单地使用 mpu.haversine_distance() 库,如下所示:

>>> import mpu
>>> munich = (48.1372, 11.5756)
>>> berlin = (52.5186, 13.4083)
>>> round(mpu.haversine_distance(munich, berlin), 1)
>>> 504.2

u
uher

请使用以下代码。

def distance(lat1, lng1, lat2, lng2):
    #return distance as meter if you want km distance, remove "* 1000"
    radius = 6371 * 1000 

    dLat = (lat2-lat1) * math.pi / 180
    dLng = (lng2-lng1) * math.pi / 180

    lat1 = lat1 * math.pi / 180
    lat2 = lat2 * math.pi / 180

    val = sin(dLat/2) * sin(dLat/2) + sin(dLng/2) * sin(dLng/2) * cos(lat1) * cos(lat2)    
    ang = 2 * atan2(sqrt(val), sqrt(1-val))
    return radius * ang

就我而言,其他代码对我来说效果不佳。所以,我只是翻译这个答案中的功能stackoverflow.com/questions/6981916/…