9

我有一些用户在我的 Django 应用程序中注册,我希望能够根据他们的邮政编码简单地计算出两个用户之间的地理距离,然后根据该邮政编码对列表进行排序。我想这个功能没有内置到 Django 中。我正在寻找一些选项,偶然发现了 geodjango,这对于我的需求来说似乎有点过分了。

4

4 回答 4

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

斯文发布的代码:

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:变量名称“距离”用词不当。该量实际上是从地球中心到输入点的两条线之间角度的余弦值。更改为“cos_x”

问题 4:不必将角度 x 转换为度数。只需将 x 乘以所选单位的地球半径(km、nm 或“法定英里”)

修复所有这些后,我们得到:

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) 到纽约 (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

于 2011-01-17T23:02:55.393 回答
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 的更正。

于 2011-01-17T18:49:53.183 回答
0

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

你可以为每个位置做方向。总距离已给出。API 似乎输出 JSON;您可以在服务器端解析答案,也可以通过 JavaScript 计算距离。

于 2011-01-17T17:48:58.303 回答
0

另一种简单的方法:

下面的函数在从邮政编码计算纬度和经度后返回两个位置之间的距离。

lat1,long1是第一个位置的纬度和经度。

lat2,long2是第二个位置的经纬度。

from decimal import Decimal
from math import sin, cos, sqrt, atan2, radians

def distance(lat1, lat2, long1, long2):
    r = 6373.0

    lat1 = radians(lat1)
    lat2 = radians(lat2)
    long1 = radians(long1)
    long2 = radians(long2)

    d_lat = lat2 - lat1
    d_long = long2 - long1

    a = (sin(d_lat/2))**2 + cos(lat1) * cos(lat2) * (sin(d_long/2))**2
    c = 2 * atan2(sqrt(a), sqrt(1-a))

    # distance in miles
    dis = r * c

    # distance in KM
    dis /= 1.609344

    return dis
于 2016-04-06T13:56:21.807 回答