使用geopy将英里转换为纬度和经度度数

2 投票
6 回答
4035 浏览
提问于 2025-04-16 07:53

背景

我想添加一个模型管理功能,用来根据距离坐标的远近来过滤查询结果。我找到了一篇博客文章,里面的代码正好实现了我想要的功能。

代码

下面的代码片段似乎使用了geopy的一些函数,但这些函数现在已经被移除。它大致上通过限制经纬度的范围来缩小查询结果。

    # Prune down the set of all locations to something we can quickly check precisely
    rough_distance = geopy.distance.arc_degrees(arcminutes=geopy.distance.nm(miles=distance)) * 2
    queryset = queryset.filter(
        latitude__range=(latitude - rough_distance, latitude + rough_distance), 
        longitude__range=(longitude - rough_distance, longitude + rough_distance)
    )

问题

由于一些使用的geopy函数已经被移除或移动,我正在尝试重写这部分代码。不过,我对这些计算不太理解——我几乎没及格过几何,而且我的研究让我更加困惑,而不是帮助我。

有没有人能帮帮我?我会非常感激的。

6 个回答

1

这段代码来自一个博客,看起来有点乱:

  def near(self, latitude=None, longitude=None, distance=None):
    if not (latitude and longitude and distance):
      return []

如果纬度是0(赤道)或者经度是0(格林威治子午线),代码会立即返回。其实应该是 if latitude is None or longitude is None .......

@TokenMacGuy的回答有改进,但:

(a) “边界框”的主要目的是为了避免SQL查询或者类似的查询去计算所有符合条件的点之间的距离。使用合适的索引,查询会执行得更快。这样做的代价是让客户端去(1)计算边界框的坐标(2)计算并检查查询返回的每个结果的准确距离。

如果省略了第2步,你会得到错误的结果,即使在赤道上也是如此。例如,“找出5英里范围内的所有披萨店”意味着你可能会得到距离达到7.07英里的答案(这是5的平方根乘以2加上5的平方根乘以2)。

注意,你展示的代码似乎是随意将半径加倍。这意味着你会得到距离14.1英里的点。

(b) 正如@TokenMacGuy所说,离赤道越远,问题就越严重。这样计算出的边界框并不包含你感兴趣的所有点——除非你真的把半径加倍了。

(c) 如果感兴趣的圆圈包括北极或南极,计算就会非常不准确,需要调整。如果感兴趣的圆圈穿过180度子午线(也就是国际日期线,不考虑锯齿状的部分),结果就会变得毫无意义;你需要检测这种情况,并进行两部分的查询(每部分针对子午线的两侧)。

关于问题(b)和(c)的解决方案,可以参考这篇文章

2

如果现在还有其他人也在关注这个问题,我之前尝试使用 geopy 时遇到了一些困难。上面提到的粗略距离计算的现代替代方法是:

import geopy
rough_distance = geopy.units.degrees(arcminutes=geopy.units.nautical(miles=1))
1

看起来,distance 的单位是英里,但它被转换成了海里。海里是根据地球的经纬度来定义的,每个海里等于一个弧度的分钟,而一个弧度的分钟又是一个弧度度数的1/60。这个值会被加倍,然后加到或减去给定的纬度和经度。这四个值可以用来形成一个围绕这些坐标的边界框。

你可以在维基百科上查找任何需要的转换因子。那里还有一篇相关的文章,标题是水平位置表示,讨论了替代经纬度定位的优缺点,这些替代方法可以避免一些复杂性。换句话说,就是在计算中用其他水平位置表示来替代经纬度时需要考虑的事项。

撰写回答