2011-01-17 27 views
8

Tengo algunos usuarios registrados en mi aplicación Django y quiero simplemente ser capaz de calcular la distancia, geográficamente, entre dos usuarios en función de su código postal y luego ordenar una lista basada en eso. Me imagino que esta funcionalidad no está integrada en Django. Estaba viendo algunas opciones y me encontré con geodjango, que parece ser excesivo para mis necesidades.Django: ¿cómo puedo encontrar la distancia entre dos ubicaciones?

+0

Aquí se presenta una [receta ActiveState] (http://code.activestate.com/recipes/393241-calculating-the-distance-between-zip-codes/) para este propósito. Es casi seguro que no está integrado en Django. –

+1

Eche un vistazo al [Proyecto de base de datos de código postal] (http://zips.sourceforge.net/), también. También proporcionan el código de Python para el cálculo de distancia en la página vinculada. –

+1

@Sven Marnach: publique el Proyecto de Base de Datos de Código Postal como una respuesta, es una buena idea. (Y sí, GeoDjango es probablemente excesivo para esto.) – tcarobruce

Respuesta

6

Siguiendo la sugerencia de tcarobruce, aquí está mi comentario anterior como una respuesta:

El Zip Code Database Project tiene una base de datos de las latitudes y longitudes de los códigos postales de EE.UU., ya sea como SQL o como CSV. También proporcionan el siguiente código para el cálculo de la distancia (editado slighlty por mí):

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 

Tenga en cuenta que el resultado se da en millas terrestres.

Edición: Correcciones debidas a John Machin.

+0

Debería haber resistido la tentación de editar ligeramente el código original sin probar el resultado. Ver mi respuesta –

0

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

Usted podría hacer instrucciones para cada ubicación. La distancia total está dada. La API parece dar salida a JSON; podría analizar la respuesta por el lado del servidor o la distancia calculada por JavaScript.

+0

Hacer esto sería contrario a los términos de servicio de Google, a menos que sea cliente de Maps for Business, por cierto. – Jordan

18

Este es un gran comentario sobre el código publicado en la respuesta (actualmente aceptada) por @Sven Marnach.

Código original de la página web del proyecto postal, la muesca editado por mí:

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 

Código publicado por 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 

Problema 1: no se ejecutará: necesita importar acos

Problema 2: RESPUESTAS INCORRECTA: necesita para convertir la diferencia de longitud a radianes en la segunda última línea

Problema 3: El nombre de la variable "distancia" es un término inapropiado. Esa cantidad es en realidad el cos del ángulo entre las dos líneas desde el centro de la tierra hasta los puntos de entrada. Cambie a "cos_x"

Problema 4: No es necesario convertir el ángulo x en grados. Simplemente multiplicar x por el radio de la Tierra en unidades elegidas (km, nm, o "millas terrestres")

Después de arreglar todo esto, obtenemos:

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 

Nota: Después de arreglar los problemas 1 y 2, este es la "ley esférica de los cosenos" como generalmente se implementa. Está bien para aplicaciones como "distancia entre dos códigos postales de EE. UU.".

Advertencia 1: No es preciso para distancias pequeñas, como desde la puerta de entrada a la calle, tanto que puede dar una distancia distinta de cero o generar una excepción (cos_x> 1.0) si los dos puntos son idénticos ; esta situación puede ser especial.

Advertencia 2: Si los dos puntos son antipodales (la ruta en línea recta pasa por el centro de la tierra), puede generar una excepción (cos_x < -1.0). Cualquiera que esté preocupado por eso puede verificar cos_x antes de hacer acos (cos_x).

Ejemplo:

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

calcDist -> 2,570.7758043869976
calc_dist -> 5,038.599866130089
calc_dist_fixed -> 2,570.9028268899356

A Sitio web del gobierno de EE. UU. (Http://www.nhc.noaa.gov/gccalc.shtml) -> 2569

Este sitio web (http://www.timeanddate.com /worldclock/distanceresult.html?p1=179 & p2 = 224), de la que me dieron las coordenadas de la OFS y la ciudad de Nueva York, -> 2577

+0

@Sven Marnach: vuelva a examinar su código. Hay exactamente dos ocurrencias de 'radianes', cada una aplicada a una latitud, ninguna a una longitud. Reexamina mi código. 'radianes' se aplica UNA VEZ a la diferencia de longitud. Intente probar los códigos y explique por qué los suyos son extraños. –

+0

@Sven Marnach: Eso es gracioso; Podría haber jurado que vi un comentario donde dijiste que mi código estaba equivocado. SO necesita un seguimiento de auditoría :-) –

+0

Perdón por eso. El comentario estuvo allí por unos segundos, pero inmediatamente noté que estaba equivocado. No pensé que hubieras cargado la página en ese pequeño marco de tiempo :) –

0

Otra manera simple:

Debajo función devuelve distancia entre dos espacios después de calcular latitudes y longitudes desde el código postal.

lat1, long1 son las latitudes y longitudes de la primera ubicación.

lat2, long2 son las latitudes y longitudes de la segunda ubicación.

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 
Cuestiones relacionadas