2011-01-17 24 views
8

Ho alcuni utenti registrati nella mia app Django e voglio semplicemente essere in grado di capire la distanza, geograficamente, tra due utenti in base al loro codice di avviamento postale e quindi ordinare un elenco basato su quello. Immagino che questa funzionalità non sia integrata in Django. Stavo guardando alcune opzioni e sono incappato in geodjango che sembra che potrebbe essere eccessivo per quello che sono i miei bisogni.Django - come posso trovare la distanza tra due posizioni?

+0

Ecco un [ricetta ActiveState] (http://code.activestate.com/recipes/393241-calculating-the-distance-between-zip-codes/) per questo scopo. Quasi certamente non è costruito in Django. –

+1

Dai un'occhiata anche al [Database Database Project] (http://zips.sourceforge.net/). Forniscono anche il codice Python per il calcolo della distanza sulla pagina collegata. –

+1

@Sven Marnach - invia il progetto di database del codice postale come risposta: è una buona soluzione. (E sì, GeoDjango è probabilmente eccessivo per questo.) – tcarobruce

risposta

6

Seguendo il suggerimento di tcarobruce, ecco il mio commento sopra come una risposta:

Il Zip Code Database Project ha un database di latitudini e longitudini dei codici di avviamento postale degli Stati Uniti, sia come SQL o in formato CSV. Essi forniscono inoltre il seguente codice per il calcolo della distanza (slighlty a cura di 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 

Nota che il risultato è dato in miglia terrestri.

Modifica: correzioni dovute a John Machin.

+0

Dovresti aver resistito alla tentazione di modificare leggermente il codice originale senza testare il risultato. Vedi la mia risposta. –

0

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

Si potrebbe fare le indicazioni per ogni posizione. La distanza totale è data. L'API sembra emettere JSON; è possibile analizzare la risposta sul lato server o calcolare la distanza calcolata da JavaScript.

+0

Fare questo sarebbe contrario ai termini di servizio di Google, a meno che tu non sia un cliente di Maps for Business, btw. – Jordan

18

Questo è un grande commento sul codice pubblicato nella risposta (attualmente accettata) da @Sven Marnach.

Codice originale dal sito web del progetto zip, con rientro a cura di 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 

codice scritto da 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: non funzionerà: ha bisogno di importare acos

Problema 2: RISPOSTE ERRATE: necessario convertire la differenza di longitudine in radianti nella penultima riga

Problema 3: il nome variabile "distanza" è un termine improprio estremo. Quella quantità è in realtà il cos dell'angolo tra le due linee dal centro della terra ai punti di ingresso. Passare a "cos_x"

Problema 4: non è necessario convertire l'angolo x in gradi. Semplicemente moltiplicare x per il raggio terrestre in unità scelti (km, nm, o "miglia terrestri")

Dopo aver sistemato tutto ciò, otteniamo:

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: Dopo aver sistemato i problemi 1 e 2, questo è la "legge sferica dei coseni" come di solito implementata. Va bene per applicazioni come "distanza tra due codici postali statunitensi".

Caveat 1: Non è preciso per piccole distanze come dalla porta di casa alla strada, tanto che può dare una distanza diversa da zero o sollevare un'eccezione (cos_x> 1.0) se i due punti sono identici ; questa situazione può essere speciale.

Avvertenza 2: Se i due punti sono antipodali (il percorso della linea retta passa attraverso il centro della terra), può sollevare un'eccezione (cos_x < -1,0). Chiunque sia preoccupato può controllare cos_x prima di fare acos (cos_x).

Esempio:

SFO (37,676, -122,433) a New York (40,733, -73,917)

calcDist -> 2.570,7758043869976
calc_dist -> 5038,599866130089
calc_dist_fixed -> 2570,9028268899356

A Sito web del governo degli Stati Uniti (http://www.nhc.noaa.gov/gccalc.shtml) -> 2569

Questo sito Web (http://www.timeanddate.com /worldclock/distanceresult.html?p1=179 & p2 = 224), da cui ho ottenuto le coordinate OFS e NYC, -> 2577

+0

@Sven Marnach: riesamina il tuo codice. Ci sono esattamente due occorrenze di 'radianti', ciascuna applicata a una latitudine, nessuna a una longitudine. Riesaminare il mio codice 'radians' viene applicato UNA VOLTA alla differenza di longitudine. Prova a testare i codici e spiega perché il tuo è l'estraneo. –

+0

@Sven Marnach: È divertente; Potrei giurare di aver visto un commento in cui hai detto che il mio codice era sbagliato. SO ha bisogno di una pista di controllo :-) –

+0

Ci scusiamo per questo. Il commento è stato lì per qualche secondo, ma ho subito notato che mi sbagliavo. Non pensavo avresti caricato la pagina in quel piccolo intervallo di tempo :) –

0

Un altro modo semplice:

Sotto funzione restituisce distanza tra due posizione dopo il calcolo latitudini e longitudini da codice postale.

lat1, long1 sono le latitudini e le longitudini della prima posizione.

lat2, long2 sono le latitudini e le longitudini della seconda posizione.

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 
Problemi correlati