Django - 如何计算两个位置之间的距离?

9 投票
4 回答
8639 浏览
提问于 2025-04-16 10:07

我在我的Django应用里有一些注册用户,我想简单地根据他们的邮政编码来计算两个用户之间的地理距离,然后根据这个距离来排序一个列表。我想这个功能在Django里可能并没有直接提供。我在寻找一些解决方案时,发现了geodjango,但感觉这个工具可能对我来说有点复杂,超出了我的需求。

4 个回答

0

http://code.google.com/apis/maps/documentation/directions/

你可以为每个地点获取路线。总的距离会被提供出来。这个接口的输出格式好像是JSON;你可以选择在服务器端解析这个答案,或者用JavaScript来计算距离。

18

这是对@Sven Marnach在(目前被接受的)答案中发布的代码的详细评论。

以下是来自zip项目网站的原始代码,我对缩进进行了编辑:

from math import *
def calcDist(lat_A, long_A, lat_B, long_B):
    distance = (sin(radians(lat_A)) *
        sin(radians(lat_B)) +
        cos(radians(lat_A)) *
        cos(radians(lat_B)) *
        cos(radians(long_A - long_B)))
    distance = (degrees(acos(distance))) * 69.09
    return distance

Sven发布的代码:

from math import sin, cos, radians, degrees

def calc_dist(lat_a, long_a, lat_b, long_b):
    lat_a = radians(lat_a)
    lat_b = radians(lat_b)
    distance = (sin(lat_a) * sin(lat_b) +
                cos(lat_a) * cos(lat_b) * cos(long_a - long_b))
    return degrees(acos(distance)) * 69.09

问题1:无法运行:需要导入acos

问题2:错误的答案:在倒数第二行需要将经度差转换为弧度。

问题3:变量名“distance”用得不太准确。这个量实际上是从地球中心到输入点的两条线之间的角度的余弦值。应该改为“cos_x”。

问题4:不需要将角度x转换为度数。只需将x乘以地球的半径,单位可以是公里、海里或“法定英里”。

修正这些问题后,我们得到:

from math import sin, cos, radians, acos

# http://en.wikipedia.org/wiki/Earth_radius
# """For Earth, the mean radius is 6,371.009 km (˜3,958.761 mi; ˜3,440.069 nmi)"""
EARTH_RADIUS_IN_MILES = 3958.761

def calc_dist_fixed(lat_a, long_a, lat_b, long_b):
    """all angles in degrees, result in miles"""
    lat_a = radians(lat_a)
    lat_b = radians(lat_b)
    delta_long = radians(long_a - long_b)
    cos_x = (
        sin(lat_a) * sin(lat_b) +
        cos(lat_a) * cos(lat_b) * cos(delta_long)
        )
    return acos(cos_x) * EARTH_RADIUS_IN_MILES

注意:修正了问题1和问题2后,这就是通常实现的“球面余弦定律”。对于像“两个美国邮政编码之间的距离”这样的应用是可以的。

注意事项1:对于像从你家门口到街道这样的小距离,它的精确度不高,甚至可能会给出一个非零的距离,或者在两个点完全相同的情况下抛出异常(cos_x > 1.0);这种情况可以特别处理。

注意事项2:如果两个点是对跖点(直线经过地球中心),可能会抛出异常(cos_x < -1.0)。如果有人对此担心,可以在调用acos(cos_x)之前检查cos_x。

示例:

从SFO(37.676, -122.433)到NYC(40.733, -73.917)

calcDist -> 2570.7758043869976
calc_dist -> 5038.599866130089
calc_dist_fixed -> 2570.9028268899356

一个美国政府网站(http://www.nhc.noaa.gov/gccalc.shtml) -> 2569

这个网站(http://www.timeanddate.com/worldclock/distanceresult.html?p1=179&p2=224),我从中获取了SFO和NYC的坐标, -> 2577

6

根据tcarobruce的建议,我把之前的评论整理成了一个回答:

邮政编码数据库项目提供了美国邮政编码的经纬度数据,可以以SQL或CSV格式下载。他们还提供了以下用于计算距离的代码(我稍微做了一些修改):

from math import sin, cos, radians, degrees, acos

def calc_dist(lat_a, long_a, lat_b, long_b):
    lat_a = radians(lat_a)
    lat_b = radians(lat_b)
    long_diff = radians(long_a - long_b)
    distance = (sin(lat_a) * sin(lat_b) +
                cos(lat_a) * cos(lat_b) * cos(long_diff))
    return degrees(acos(distance)) * 69.09

请注意,计算结果是以法定英里为单位的。

编辑:感谢John Machin的指正。

撰写回答