PHP è SQL: Calculate o Interrogate a Grande Distanza di Circulu Tra i Punti di Latitudine è Longitudine Cù a Formula Haversine

Formula Haversine - Calculate a Grande Distanza di Circulu cù PHP o MySQL

Stu mese aghju prugramatu un pocu in PHP è MySQL in quantu à SIG. Snooping intornu à a rete, aghju avutu in realtà un tempu difficile per truvà alcuni di i Càlculi geografichi per truvà a distanza trà dui lochi allora aghju vulsutu sparte li quì.

Mappa di Volu in Europa Cù Distanza di Grande Circulu

U modu simplice di calculà una distanza trà dui punti hè di aduprà a formula Pitagorica per calculà l'ipotenusa di un triangulu (A² + B² = C²). Questu hè cunnisciutu cum'è u Distanza euclidiana.

Hè un principiu interessante ma ùn si applica micca cù a Geografia postu chì a distanza trà e linee di latitudine è di longitudine hè micca una distanza uguale apartu. Quandu si avvicinavanu à l'equatore, e linee di latitudine si alluntanu. Se utilizate un tipu d'equazione di triangulazione simplice, pò misurà a distanza cun precisione in un locu è terribilmente sbagliatu in l'altru, per via di a curvatura di a Terra.

Distanza Gran Circulu

I percorsi chì sò percorsi longu distanzi intornu à a Terra sò cunnisciuti cum'è Distanza Gran Circulu. Hè cusì ... a distanza più corta trà dui punti nantu à una sfera hè diversa da i punti in una carta piatta. Unite questu cù u fattu chì e linee di latitudine è longitudine ùn sò micca equidistanti ... è avete un calculu difficiule.

Eccu una fantastica spiegazione video di u funziunamentu di i Grandi Circuli.

A Formula Haversine

A distanza aduprendu a curvatura di a Terra hè incorporata in u Formula Haversine, chì adopra a trigonometria per permette a curvatura di a terra. Quandu truvate a distanza trà 2 posti in a terra (à volu di linea), una linea dritta hè veramente un arcu.

Questu hè applicabile in u volu aereu - avete mai guardatu a carta vera di i voli è avete rimarcatu chì sò arcuati? Hè perchè hè più cortu volà in un arcu trà dui punti chì direttamente à u locu.

PHP: Calculate a Distanza trà 2 Punti di Latitudine è Longitudine

In ogni casu, eccu a formula di PHP per calculà a distanza trà dui punti (cù a cunversione Mile vs. Kilometer) arrotondata à dui decimali.

function getDistanceBetweenPointsNew($latitude1, $longitude1, $latitude2, $longitude2, $unit = 'miles') {
  $theta = $longitude1 - $longitude2; 
  $distance = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))) + (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta))); 
  $distance = acos($distance); 
  $distance = rad2deg($distance); 
  $distance = $distance * 60 * 1.1515; 
  switch($unit) { 
    case 'miles': 
      break; 
    case 'kilometers' : 
      $distance = $distance * 1.609344; 
  } 
  return (round($distance,2)); 
}

SQL: Recuperà Tutti i Registri In Un Campu Calculendu a Distanza In Miglia Usendu a Latitudine è a Longitudine

Hè ancu pussibule di aduprà SQL per fà un calculu per truvà tutti i registri in una distanza specifica. In questu esempiu, aghju da dumandà MyTable in MySQL per truvà tutti i registri chì sò menu o uguali à $ distanza variabile (in Miglia) da a mo situazione à $ latitudine è $ longitudine:

A dumanda per recuperà tutti i registri in un specificu luntanu calculendu a distanza in miglia trà dui punti di latitudine è longitudine sò:

$query = "SELECT *, (((acos(sin((".$latitude."*pi()/180)) * sin((`latitude`*pi()/180)) + cos((".$latitude."*pi()/180)) * cos((`latitude`*pi()/180)) * cos(((".$longitude."- `longitude`)*pi()/180)))) * 180/pi()) * 60 * 1.1515) as distance FROM `table` WHERE distance <= ".$distance."

Avete bisognu di persunalizà questu:

  • $ longitudine - questu hè una variabile PHP induve passu a longitudine di u puntu.
  • $ latitudine - questu hè una variabile PHP induve passu a longitudine di u puntu.
  • $ distanza - questa hè a distanza chì vulete truvà tutti i registri menu o uguali à.
  • tàvula - questu hè u tavulinu ... vi vulete rimpiazzà quellu cù u vostru nome di tavulinu.
  • latitudine - questu hè u campu di a vostra latitudine.
  • longitudine - questu hè u campu di a vostra longitudine.

SQL: Recuperà Tutti i Registri In Un Campu Calculendu a Distanza In Chilometri Usendu a Latitudine è a Longitudine

Eccu a dumanda SQL aduprendu chilometri in MySQL:

$query = "SELECT *, (((acos(sin((".$latitude."*pi()/180)) * sin((`latitude`*pi()/180)) + cos((".$latitude."*pi()/180)) * cos((`latitude`*pi()/180)) * cos(((".$longitude."- `longitude`) * pi()/180)))) * 180/pi()) * 60 * 1.1515 * 1.609344) as distance FROM `table` WHERE distance <= ".$distance."

Avete bisognu di persunalizà questu:

  • $ longitudine - questu hè una variabile PHP induve passu a longitudine di u puntu.
  • $ latitudine - questu hè una variabile PHP induve passu a longitudine di u puntu.
  • $ distanza - questa hè a distanza chì vulete truvà tutti i registri menu o uguali à.
  • tàvula - questu hè u tavulinu ... vi vulete rimpiazzà quellu cù u vostru nome di tavulinu.
  • latitudine - questu hè u campu di a vostra latitudine.
  • longitudine - questu hè u campu di a vostra longitudine.

Aghju utilizatu stu codice in una piattaforma di mappatura d'impresa chì avemu utilizatu per un magazinu cù più di 1,000 posti in l'America di u Nordu è hà travagliatu bellu.

76 Comments

  1. 1

    Ti ringraziu assai per sparte. Hè statu un travagliu di copia è incolla faciule è funziona bè. Mi hai salvatu assai tempu.
    FYI per chiunque porti in C:
    doppiu deg2rad (doppiu deg) {ritornu deg * (3.14159265358979323846 / 180.0); }

  2. 2

    Un bellu pezzu di publicazione - hà travagliatu assai bellu - Aviu solu da cambià u nome di a tavula chì tene u lat-long. Funziona abbastanza veloce per .. Aghju un numeru ragiunevolmente chjucu di lat-longs (<400) ma pensu chì questu scala bè. Ancu un bellu situ - l'aghju appena aghjuntu à u mo contu del.icio.us è rivederaghju regolarmente.

  3. 4
  4. 5

    Aghju cercatu tuttu u ghjornu per calculi di distanza è aghju trovu l'algoritmu harversine, grazie à voi per avè datu l'esempiu nantu à cumu mette in una dichjarazione sql. Grazie è saluta, Daniel

  5. 8

    Pensu chì u vostru SQL abbia bisognu di una dichjarazione.
    invece di WHERE distanza <= $ distanza chì pudete avè bisognu
    aduprate AVÈ distanza <= $ distanza

    altrimenti grazie per avermi risparmiatu una mansa di tempu è energia.

  6. 10
  7. 11
  8. 12

    Grazie mille per sparte stu codice. Mi hà risparmiatu assai tempu di sviluppu. Inoltre, grazie à i vostri lettori per avè indicatu chì una dichjarazione HAVING hè necessaria per MySQL 5.x. Assai utile.

  9. 14
  10. 15

    Bonghjornu,

    Un'altra dumanda. Ci hè una formula per e stringhe NMEA cum'è quella sottu?

    1342.7500, N, 10052.2287, E

    $GPRMC,032731.000,A,1342.7500,N,10052.2287,E,0.40,106.01,101106,,*0B

    Grazie,
    Harry

  11. 16

    Aghju ancu trovu chì DOVE ùn hà micca travagliatu per mè. Hè cambiatu in HAVING è tuttu funziona perfettu. À u primu ùn aghju micca lettu i cumenti è riscrittu cù una selezzione annidata. Tramindui funzioneranu bè.

  12. 17
  13. 18

    Incredibilmente utile, grazie assai! Aviu avutu qualchì prublema cù u novu "AVE", piuttostu ch'è "DOVE", ma una volta chì aghju lettu i cummenti quì (dopu à una mezz'ora di grinchendu i denti in frustrazione = P), l'aghju fattu travaglià bè. Ti ringraziu ^ _ ^

  14. 19
  15. 20

    Tenite à mente chì una dichjarazione selezziunata cum'è quella serà assai computazionale intensa è dunque lenta. Se avete assai di queste dumande, pò impastughjassi abbastanza rapidamente.

    Un approcciu assai menu intensu hè di lancià una prima selezzione (grezza) aduprendu una zona SQUARE definita da una distanza calculata cioè "selezziunà * da u nome di tavuletta induve a latitudine trà lat1 è lat2 è a longitudine trà lon1 è lon2". lat1 = targetlatitude - latdiff, lat2 = targetlatitude + latdiff, simile à lon. latdiff ~ = distanza / 111 (per km), o distanza / 69 per miglia postu chì 1 gradu di latitudine hè ~ 111 km (poca variazione postu chì a terra hè un pocu ovale, ma basta à stu scopu). londiff = distanza / (abs (cos (deg2rad (latitudine)) * 111)) - o 69 per miglia (pudete in realtà piglià un quadru leggermente più grande per tene contu di variazioni). Poi piglià u risultatu di questu è alimentallu in a selezzione radiale. Ùn vi scurdate solu di tene contu di e coordinate fora di i limiti - vale à dì a gamma di longitudine accettabile hè -180 à +180 è a gamma di latitudine accettabile hè da -90 à +90 - in casu chì u vostru latdiff o londra corre fora di sta gamma . Nutate bè chì in a maiò parte di i casi questu ùn pò micca esse applicabile postu chì affetta solu i calculi annantu à una linea attraversu l'oceanu Pacificu da u polu à u polu, ancu s'ellu interseca una parte di chukotka è una parte di Alaska.

    Ciò chì rializemu da questu hè una riduzione significativa di u numeru di punti contr'à i quali fate stu calculu. Se avete un milione di punti globali in a basa di dati distribuiti à pocu pressu in modu uniforme è vulete cercà in 100 km, allora a vostra prima ricerca (rapida) hè di una superficie di 10000 km quadrati è darà probabilmente circa 20 risultati (basatu annantu à una distribuzione uniforme in un superficia di circa 500 M sq km), chì significa chì eseguite u calculu di distanza cumplessa 20 volte per questa dumanda invece di un milione di volte.

    • 21

      Sbagliu minore in l'esempiu ... chì seria per 50 km (micca 100) postu chì guardemu à u "raghju" di u nostru ... quadratu.

      • 22

        Fantasticu cunsigliu! In realtà aghju travagliatu cù un sviluppatore chì hà scrittu una funzione chì hà tiratu u quadru internu è dopu una funzione ricorsiva chì hà fattu "quadri" intornu à u perimetru per includere è escludere i punti restanti. U risultatu hè statu un risultatu incredibilmente veloce - puderia valutà milioni di punti in microsecondi.

        U mo approcciu sopra hè definitivamente 'grezzu' ma capace. Grazie dinò!

        • 23

          Doug,

          Aghju pruvatu à aduprà mysql è php per valutà se un puntu longu hè in un poligonu. Sapete sì u vostru amicu sviluppatore hà publicatu alcuni esempi nantu à cume realizà questu compitu. O cunnosci qualchì bon esempiu. Grazie in anticipiu.

  16. 24

    Salute à tutti questu hè a mo dichjarazione SQL di prova:

    SELECT DISTINCT area_id, (
    (
    (
    acos( sin( ( 13.65 * pi( ) /180 ) ) * sin( (
    `lat_dec` * pi( ) /180 ) ) + cos( ( 13.65 * pi( ) /180 ) ) * cos( (
    `lat_dec` * pi( ) /180 )
    ) * cos( (
    ( 51.02 - `lon_dec` ) * pi( ) /180 )
    )
    )
    ) *180 / pi( )
    ) *60 * 1.1515 * 1.609344
    ) AS distance
    FROM `post_codes` WHERE distance <= 50

    è Mysql mi dice chì a distanza, ùn esiste micca cum'è colonna, possu aduprà l'ordine per, a possu fà senza DOVE, è funziona, ma micca cun ella ...

  17. 26

    Questu hè grande, però hè cum'è l'uccelli volanu. Sarebbe fantasticu di pruvà à integrà l'API di google maps in questu modu (forse aduprendu strade ecc.) Solu per dà una idea aduprendu una forma diversa di trasportu. Aghju ancu da fà una funzione simulata di ricottura in PHP chì puderebbe offre una suluzione efficace à u prublema di vinditori viaghjatori. Ma pensu chì possu esse capace di riutilizà un pocu di u vostru còdice per falla.

  18. 27
  19. 28

    Bon articulu! Aghju trovu assai articuli chì descrivenu cume calculà a distanza trà dui punti ma eru veramente in cerca di u frammentu SQL.

  20. 29
  21. 30
  22. 31
  23. 32
  24. 36

    2 ghjorni di ricerca per truvà infine sta pagina chì risolve u mo prublema. Sembra cum'è megliu arruinà u mo WolframAlpha è scuppià e mo matematiche. U cambiamentu da DOVE à AVÈ hà u mo script in funziunamentu. À RINGRAZIÀ TI

  25. 37
    • 38

      Grazie Georgi. Aghju continuatu à truvà a colonna 'distanza' micca truvata. Una volta aghju cambiatu u DOVE À AVÈ hà travagliatu cum'è un incantu!

  26. 39

    Vogliu chì questa sia a prima pagina chì avessi trovu annantu à questu. Dopu pruvatu parechje cumandamenti sfarenti questu era l'unicu à travaglià bè, è cù cambiamenti minimi necessarii per adattassi à a mo propria basa di dati.
    À ringrazià assai!

  27. 40

    Vogliu chì questa sia a prima pagina chì avessi trovu annantu à questu. Dopu pruvatu parechje cumandamenti sfarenti questu era l'unicu à travaglià bè, è cù cambiamenti minimi necessarii per adattassi à a mo propria basa di dati.
    À ringrazià assai!

  28. 41
  29. 42
  30. 43
  31. 45
  32. 46
  33. 47

    Sò chì sta formula funziona, ma ùn possu micca vede induve si tene contu di u raghju di a terra. Qualchissia mi pò illuminà, per piacè?

  34. 49
  35. 50
  36. 52

    Grazie Douglas, a Quistione SQL hè esattamente ciò chì avia bisognu, è aghju pensatu chì averia da scrive da mè. Mi hai salvatu da forse ore di curva d'apprendimentu di longitudine di latitudine!

  37. 53
  38. 55
  39. 56
  40. 58

    grazie per a publicazione di questu articulu utile,  
    ma per qualchì ragione mi piacerebbe dumandà
    cumu uttene a distanza trà i coords in mysql db è i coords inseriti in php da l'utente?
    per discrive più chjaramente:
    1. l'utilizatore deve inserisce [id] per selezziunà i dati specificati da db è i cuncordi di l'utilizatore stessu
    2.u fugliale php uttene i dati di destinazione (coords) aduprendu [id] è poi calcula a distanza trà l'utente è u puntu di destinazione

    o pò solu ottene solu a distanza da u codice sottu?

    $ qry = "SELECT *, (((acos (sin ((". $ latitude. "* pi () / 180)) * sin ((` Latitude` * pi () / 180)) + cos ((". $ latitude. "* pi () / 180)) * cos ((` Latitude` * pi () / 180)) * cos (((". $ longitude." - `Longitude`) * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) cum'è distanza FROM `MyTable` WHERE distance> =". $ Distance. " >>>> possu "caccià" a distanza da quì?
    grazie dinò,
    Timmy S

  41. 60

    ok, tuttu ciò chì aghju pruvatu ùn funziona. Vogliu dì, ciò chì aghju funziona, ma e distanze sò luntane.

    Qualchissia puderia forse vede ciò chì ùn va micca in questu codice?

    if (isset ($ _ POST ['inviatu'])) {$ z = $ _POST ['codice postale']; $ r = $ _POST ['radius']; echo "Risultati per". $ z; $ sql = mysql_query ("SELECT DISTINCT m.zipcode, m.MktName, m.LocAddSt, m.LocAddCity, m.LocAddState, m.x1, m.y1, m.verified, z1.lat, z2.lon, z1. cità, z1.state FROM mrk m, zip z1, zip z2 WHERE m.zipcode = z1.zipcode AND z2.zipcode = $ z AND (3963 * acos (truncate (sin (z2.lat / 57.2958) * sin (m. y1 / 57.2958) + cos (z2.lat / 57.2958) * cos (m.y1 / 57.2958) * cos (m.x1 / 57.2958 - z2.lon / 57.2958), 8))) <= $ r ") o morire (mysql_error ()); while ($ fila = mysql_fetch_array ($ sql)) {$ store1 = $ fila ['MktName']. ""; $ magazinu = $ fila ['LocAddSt']. ""; $ store. = $ fila ['LocAddCity']. ",". $ fila ['LocAddState']. " ". $ Fila ['codice postale']; $ latitude1 = $ fila ['lat']; $ longitude1 = $ fila ['lon']; $ latitude2 = $ fila ['y1']; $ longitude2 = $ fila ['x1']; $ cità = $ fila ['cità']; $ state = $ fila ['state']; $ dis = getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi'); // $ dis = distanza ($ lat1, $ lon1, $ lat2, $ lon2); $ verificatu = $ fila ['verificatu']; if ($ verificatu == '1') {echo ""; echo "". $ store. ""; echo $ dis. "Mile (s) away"; echo ""; } altru {echu "". $ store. ""; echo $ dis. "Mile (s) away"; echo ""; }}}

    u mo codice functions.php
    funzione getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi') {$ theta = $ longitude1 - $ longitude2; $ distanza = (sin (deg2rad ($ latitude1)) * sin (deg2rad ($ latitude2))) + (cos (deg2rad ($ latitude1)) * cos (deg2rad ($ latitude2)) * cos (deg2rad ($ theta)) ); $ distanza = acos ($ distanza); $ distanza = rad2deg ($ distanza); $ distanza = $ distanza * 60 * 1.1515; cambià ($ unità) {casu 'Mi': rumpitura; casu 'Km': $ distanza = $ distanza * 1.609344; } ritornu (giru ($ distanza, 2)); }

    Ti ringraziu in anticipu

  42. 61
  43. 62

    Ehi Douglas, grande articulu. Aghju trovu a vostra spiegazione di i cuncetti geografichi è u codice veramente interessante. U mo solu suggerimentu seria di spaziu è rintra u codice per a visualizazione (cum'è Stackoverflow, per esempiu). Capiscu chì vulete cunservà u spaziu, ma a spaziatura / indentazione di codice cunvenziunale mi renderebbe assai più faciule per mè, cum'è prugrammatore, di leghje è di sezziunà. In ogni casu, hè una piccula cosa. Continuate u grande travagliu.

  44. 64
  45. 65

    quì mentre aduprendu cù a funzione uttenemu un tippu di distanza .. mentre aduprendu a dumanda vene u so altru tippu di distanza

  46. 66
  47. 67
  48. 68
  49. 69
  50. 70

    pare più veloce (mysql 5.9) aduprà duie volte a formula in a selezzione è induve:
    $ formula = "(((acos (sin ((". $ latitude. "* pi () / 180)) * sin ((` Latitude` * pi () / 180)) + cos ((". $ latitude. "* Pi () / 180)) * cos ((` Latitude` * pi () / 180)) * cos (((". $ Longitude." - `Longitude`) * pi () / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) ";
    $ sql = 'SELECT *,'. $ formula. ' quant'è a distanza DA u tavulinu DOVE '.. $ formula.' <= '. $ distanza;

  51. 71
  52. 72

    Grazie mille per tonde stu articulu. Hè assai utile.
    PHP hè statu inizialmente creatu cum'è una piattaforma di scrittura simplice chjamata "Pagina Persunale". Oghje ghjornu PHP (a abbreviazione di Hypertext Preprocessor) hè una alternativa di a tecnulugia Active Server Pages (ASP) di Microsoft.

    PHP hè una lingua open-side di u servitore chì hè aduprata per creà pagine web dinamiche. Pò esse incrustatu in HTML. PHP hè di solitu usatu in cungiunzione cù una basa di dati MySQL nantu à i servitori web Linux / UNIX. Hè probabilmente a lingua di scrittura più famosa.

  53. 73

    Aghju trovu sopra a soluzione chì ùn funziona micca bè.
    Ho bisognu di cambià in:

    $ qqq = "SELECT *, (((acos (sin ((". $ latitude. "* pi () / 180)) * sin ((` latt` * pi () / 180)) + cos ((". $ latitude. "* pi () / 180)) * cos ((` latt` * pi () / 180)) * cos (((". $ longitude." - `longt`) * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515) cum'è distanza FROM `register`“;

  54. 75
  55. 76

    Ciao, per piacè averaghju veramente bisognu di u vostru aiutu nantu à questu.

    Aghju fattu una dumanda di uttene à u mo servitore web http://localhost:8000/users/findusers/53.47792/-2.23389/20/
    53.47792 = $ latitudine
    -2.23389 = $ longitudine
    è 20 = a distanza chì vogliu recuperà

    Tuttavia aduprendu voi formula, recupera tutte e righe in u mo db

    $ risultati = DB :: select (DB :: raw ("SELECT *, (((acos (sin ((". $ latitude. "* pi () / 180)) * sin ((lat * pi () / 180 )) + cos ((". $ latitude." * pi () / 180)) * cos ((lat * pi () / 180)) * cos (((.. $ longitudine. "- lng) * pi ( ) / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) cum'è distanza FROM markers HAVING distance> = ". $ Distance));

    [{"Id": 1, "name": "Frankie Johnnie & Luigo Too", "address": "939 W El Camino Real, Mountain View, CA", "lat": 37.386337280273, "lng": - 122.08582305908, "Distance": 16079.294719663}, {"id": 2, "name": "Amici's East Coast Pizzeria", "address": "790 Castro St, Mountain View, CA", "lat": 37.387138366699, "lng": -122.08323669434, "distance": 16079.175940152}, {"id": 3, "name": "Kapp's Pizza Bar & Grill", "address": "191 Castro St, Mountain View, CA", "lat": 37.393886566162, "Lng": - 122.07891845703, "distance": 16078.381373826}, {"id": 4, "name": "Pizza à Table Ronde: Mountain View", "address": "570 N Shoreline Blvd, Mountain View, CA", "Lat": 37.402652740479, "lng": - 122.07935333252, "distance": 16077.420540582}, {"id": 5, "name": "Tony & Alba's Pizza & Pasta", "address": "619 Escuela Ave, Mountain View, CA "," lat ": 37.394012451172," lng ": - 122.09552764893," distance ": 16078.563225154}, {" id ": 6," name ":" Oregano's Wood-Fired Pizza "," address ":" 4546 El Camino Real, Los Altos, CA "," lat ": 37.401725769043," lng ": - 122.11464691162," distance ": 16077.937560795}, {" id ": 7," name ":" I bars è grills "," address ":" 24 Whiteley Street, Manchester "," lat ": 53.485118865967," lng ": - 2.1828699111938," distance ": 8038.7620112314}]

    Vogliu recuperà solu file cù 20 miglia ma porta tutte e file. Per piacè chì facciu male

Chì ne pensi?

Stu situ utilizeghja Akismet per reducisce u puzzicheghju. Sapete ciò chì i dati di i vostri dati è processatu.