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 functionality 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.
-
Here is an [ActiveState recipe](http://code.activestate.com/recipes/393241-calculating-the-distance-between-zip-codes/) for this purpose. It almost certainly isn't built into Django. – Sven Marnach Jan 17 '11 at 17:38
-
1Have a look at the [Zip Code Database Project](http://zips.sourceforge.net/), too. They also provide Python code for distance calculation on the linked page. – Sven Marnach Jan 17 '11 at 17:41
-
1@Sven Marnach - post the Zip Code Database Project as an answer - it's a good one. (And yes, GeoDjango is probably overkill for this.) – tcarobruce Jan 17 '11 at 18:01
-
this might be what i need thanks! – JPC Jan 17 '11 at 18:19
-
The code in the answer that you accepted is **BUGGY**; see my answer. – John Machin Jan 17 '11 at 23:07
4 Answers
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

- 81,303
- 11
- 141
- 189
-
@Sven Marnach: Re-examine your code. There are exactly two occurences of 'radians', each applied to a latitude, none to a longitude. Re-examine my code. `radians` is applied ONCE to the longitude difference. Try testing the codes and explain why yours is the odd-man-out. – John Machin Jan 17 '11 at 23:34
-
@Sven Marnach: That's funny; I could have sworn I saw a comment where you said my code was wrong. SO needs an audit trail :-) – John Machin Jan 17 '11 at 23:35
-
Sorry for that. The comment was there for a few seconds, but I immediately noticed I was wrong. Did not think you would have loaded the page in that tiny time frame :) – Sven Marnach Jan 18 '11 at 00:53
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.

- 574,206
- 118
- 941
- 841
-
You should have resisted the temptation to slightly edit the original code without testing the result. See my answer. – John Machin Jan 17 '11 at 23:05
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

- 3,351
- 10
- 31
- 48
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.

- 429
- 4
- 10
-
Doing this would be against Google's terms of service, unless you are a Maps for Business customer, btw. – Jordan Mar 09 '12 at 19:07