开发者

Django - how can I find the distance between two locations?

I have some users registered in my Django app and I want to simply be able to figure out the distance, geographically, between two users based on their zip code and then sort a list based on that. I would imagine this function开发者_如何转开发ality isn't built into Django. I was looking at some options and stumbled across geodjango which seems like it might be overkill for what my needs are.


This is a big fat comment on the code posted in the (currently-accepted) answer by @Sven Marnach.

Original code from zip project website, with indentation edited by me:

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

Code posted by 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

Problem 1: WON'T RUN: needs to import acos

Problem 2: WRONG ANSWERS: needs to convert the longitude difference to radians in the second last line

Problem 3: The variable name "distance" is an extreme misnomer. That quantity is actually the cos of the angle between the two lines from the centre of the earth to the input points. Change to "cos_x"

Problem 4: It is not necessary to convert angle x to degrees. Simply multiply x by earth's radius in chosen units (km, nm, or "statute miles")

After fixing all that, we get:

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

Note: After fixing problems 1 and 2, this is the "spherical law of cosines" as usually implemented. It is OK for applications like "distance between two US zipcodes".

Caveat 1: It is not precise for small distances like from your front door to the street, so much so that it can give a non-zero distance or raise an exception (cos_x > 1.0) if the two points are identical; this situation can be special-cased.

Caveat 2: If the two points are antipodal (straight line path passes through the center of the earth), it can raise an exception (cos_x < -1.0). Anyone worried about that can check cos_x before doing acos(cos_x).

Example:

SFO (37.676, -122.433) to NYC (40.733, -73.917)

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

A US government website (http://www.nhc.noaa.gov/gccalc.shtml) -> 2569

This website (http://www.timeanddate.com/worldclock/distanceresult.html?p1=179&p2=224), from which I got the SFO and NYC coordinates, -> 2577


Following tcarobruce's suggestion, here is my above comment as an answer:

The Zip Code Database Project has a database of the latitudes and longitudes of the US zip codes, either as SQL or as CSV. They also provide the following code for distance calculation (slighlty edited by me):

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

Note that the result is given in statute miles.

Edit: Corrections due to John Machin.


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

You could do directions for each location. The total distance is given. The API seems to output JSON; you could either parse the answer on the server side or have the distance calculated by JavaScript.


Another simple way:

Below function returns distance between two location after calculating latitudes and longitudes from zipcode.

lat1, long1 are the latitudes and longitudes of first location.

lat2, long2 are the latitudes and longitudes of second location.

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜