PHP și SQL: Calculați sau interogați distanța cercului mare între punctele de latitudine și longitudine cu formula Haversine

Formula Haversine - Calculați distanța cercului mare cu PHP sau MySQL
Timp de citit: 3 minute

Luna aceasta am programat destul de mult în PHP și MySQL în ceea ce privește GIS. Trăgând prin net, de fapt am găsit cu greu unele dintre Calcule geografice pentru a găsi distanța dintre două locații, așa că am vrut să le împărtășesc aici.

Harta de zbor Europa cu distanța cercului mare

Modul simplu de a calcula distanța dintre două puncte este utilizarea formulei pitagorice pentru a calcula ipotenuza unui triunghi (A² + B² = C²). Acest lucru este cunoscut sub numele de distanta euclidiana.

Acesta este un început interesant, dar nu se aplică cu Geografia, deoarece distanța dintre liniile de latitudine și longitudine este nu o distanță egală în afară. Pe măsură ce vă apropiați de ecuator, liniile de latitudine se îndepărtează. Dacă utilizați un fel de ecuație simplă de triunghi, aceasta poate măsura distanța cu precizie într-o locație și teribil de greșită în cealaltă, din cauza curburii Pământului.

Distanța cercului mare

Rutele parcurse pe distanțe lungi în jurul Pământului sunt cunoscute sub numele de Distanța cercului mare. Adică ... cea mai mică distanță dintre două puncte pe o sferă este diferită de punctele dintr-o hartă plană. Combinați acest lucru cu faptul că liniile de latitudine și longitudine nu sunt echidistante ... și aveți un calcul dificil.

Iată o explicație video fantastică despre modul în care funcționează Cercurile mari.

Formula Haversine

Distanța utilizând curbura Pământului este încorporată în Formula Haversine, care folosește trigonometria pentru a permite curbura pământului. Când găsiți distanța dintre 2 locuri de pe pământ (în linie dreaptă), o linie dreaptă este într-adevăr un arc.

Acest lucru este aplicabil în zborul aerian - v-ați uitat vreodată pe harta reală a zborurilor și ați observat că sunt arcuite? Asta pentru că este mai scurt să zbori într-o arcadă între două puncte decât direct către locație.

PHP: Calculați distanța dintre 2 puncte de latitudine și longitudine

Oricum, iată formula PHP pentru calcularea distanței dintre două puncte (împreună cu conversia Mile vs. Kilometri) rotunjite la două zecimale.

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: Recuperarea tuturor înregistrărilor într-un interval prin calcularea distanței în mile folosind latitudinea și longitudinea

De asemenea, este posibil să utilizați SQL pentru a face un calcul pentru a găsi toate înregistrările la o anumită distanță. În acest exemplu, voi interoga MyTable în MySQL pentru a găsi toate înregistrările care sunt mai mici sau egale cu variabila $ distanță (în mile) până la locația mea la $ latitudine și $ longitudine:

Interogarea pentru recuperarea tuturor înregistrărilor dintr-un anumit distanţă prin calcularea distanței în mile între două puncte de latitudine și longitudine sunt:

$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."

Va trebui să personalizați acest lucru:

  • $ longitudine - aceasta este o variabilă PHP în care trec longitudinea punctului.
  • $ latitudine - aceasta este o variabilă PHP în care trec longitudinea punctului.
  • $ distanta - aceasta este distanța la care doriți să găsiți toate înregistrările mai mici sau egale.
  • tabel - acesta este tabelul ... veți dori să îl înlocuiți cu numele tabelei.
  • latitudine - acesta este câmpul latitudinii tale.
  • longitudine - acesta este domeniul longitudinii tale.

SQL: Recuperarea tuturor înregistrărilor într-un interval prin calcularea distanței în kilometri utilizând latitudinea și longitudinea

Iată interogarea SQL folosind kilometri în 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."

Va trebui să personalizați acest lucru:

  • $ longitudine - aceasta este o variabilă PHP în care trec longitudinea punctului.
  • $ latitudine - aceasta este o variabilă PHP în care trec longitudinea punctului.
  • $ distanta - aceasta este distanța la care doriți să găsiți toate înregistrările mai mici sau egale.
  • tabel - acesta este tabelul ... veți dori să îl înlocuiți cu numele tabelei.
  • latitudine - acesta este câmpul latitudinii tale.
  • longitudine - acesta este domeniul longitudinii tale.

Am folosit acest cod într-o platformă de cartografiere a întreprinderii pe care am folosit-o pentru un magazin de vânzare cu amănuntul cu peste 1,000 de locații din America de Nord și a funcționat frumos.

4 Comentarii

  1. 1

    Vă mulțumesc foarte mult pentru distribuire. Aceasta a fost o lucrare de copiere și lipire ușoară și funcționează excelent. M-ai salvat mult timp.
    FYI pentru oricine portează în C:
    dublu deg2rad (dublu deg) {return deg * (3.14159265358979323846 / 180.0); }

  2. 2

    Foarte frumoasă postare - a funcționat foarte frumos - a trebuit doar să schimb numele tabelului care deține lat-long. Funcționează destul de repede până la .. Am un număr destul de mic de laturi lungi (<400), dar cred că acest lucru ar escalada frumos. Și site frumos - tocmai l-am adăugat în contul meu del.icio.us și voi reveni în mod regulat.

  3. 4
  4. 5

    Am căutat întreaga zi pentru calculele distanței și am găsit algoritmul harversine, mulțumită pentru că ați dat exemplul despre cum să-l puneți într-o declarație sql. Mulțumesc și salut, Daniel

  5. 8

    Cred că SQL-ul dvs. are nevoie de o declarație având.
    în loc de WHERE distanță <= $ distanță de care s-ar putea să aveți nevoie
    utilizați distanță având <= $ distanță

    altfel mulțumesc că mi-ai economisit o grămadă de timp și energie.

  6. 10
  7. 11
  8. 12

    Vă mulțumim mult pentru că ați distribuit acest cod. Mi-a economisit mult timp de dezvoltare. De asemenea, mulțumesc cititorilor dvs. pentru că ați subliniat că este necesară o declarație HAVING pentru MySQL 5.x. Foarte folositor.

  9. 14
  10. 15

    Buna,

    Alta intrebare. Există o formulă pentru șirurile NMEA ca cea de mai jos?

    1342.7500, N, 10052.2287, E

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

    Multumesc,
    Harry

  11. 16

    De asemenea, am constatat că WHERE nu a funcționat pentru mine. L-am schimbat în HAVING și totul funcționează perfect. La început nu am citit comentariile și le-am rescris folosind o selecție imbricată. Ambele vor funcționa foarte bine.

  12. 17
  13. 18

    Incredibil de util, vă mulțumesc foarte mult! Aveam niște probleme cu noul „HAVING”, mai degrabă decât „WHERE”, dar odată ce am citit comentariile aici (după aproximativ o jumătate de oră de scrâșnit din dinți de frustrare = P), am reușit să funcționeze frumos. Mulțumesc ^ _ ^

  14. 19
  15. 20

    Rețineți că o astfel de afirmație selectă va fi foarte intensă din punct de vedere al calculului și, prin urmare, lentă. Dacă aveți o mulțime de aceste întrebări, aceasta poate împiedica lucrurile destul de repede.

    O abordare mult mai puțin intensă este să executați o primă selecție (brută) folosind o zonă SQUARE definită de o distanță calculată, adică „selectați * din numele tabelului unde latitudinea dintre lat1 și lat2 și longitudinea între lon1 și lon2”. lat1 = targetlatitude - latdiff, lat2 = targetlatitude + latdiff, similar cu lon. latdiff ~ = distanță / 111 (pentru km), sau distanță / 69 pentru mile, deoarece 1 grad de latitudine este de ~ 111 km (ușoară variație, deoarece pământul este ușor oval, dar suficient în acest scop). londiff = distance / (abs (cos (deg2rad (latitude)) * 111)) - sau 69 pe mile (puteți lua de fapt un pătrat ceva mai mare pentru a ține cont de variații). Apoi luați rezultatul și introduceți-l în selecția radială. Nu uitați să țineți cont de coordonatele în afara limitelor - adică intervalul de longitudine acceptabilă este -180 până la +180 și intervalul de latitudine acceptabilă este de -90 la +90 - în cazul în care latdiff-ul sau londra dvs. se află în afara acestui interval . Rețineți că, în majoritatea cazurilor, acest lucru nu poate fi aplicabil, deoarece afectează numai calculele pe o linie prin oceanul Pacific de la pol la pol, deși intersectează o parte din chukotka și o parte din Alaska.

    Ceea ce realizăm prin aceasta este o reducere semnificativă a numărului de puncte împotriva cărora faceți acest calcul. Dacă aveți un milion de puncte globale în baza de date distribuite aproximativ uniform și doriți să căutați în 100 km, atunci prima dvs. căutare (rapidă) are o suprafață de 10000 km pătrați și va produce probabil aproximativ 20 de rezultate (pe baza unei distribuții uniforme pe o suprafață de aproximativ 500 milioane km pătrați), ceea ce înseamnă că executați calculul distanței complexe de 20 de ori pentru această interogare în loc de un milion de ori.

    • 21

      O mică greșeală în exemplu ... care ar fi pentru mai puțin de 50 km (nu 100) deoarece ne uităm la „raza” ... pătratului nostru.

      • 22

        Sfaturi fantastice! De fapt, am lucrat cu un dezvoltator care a scris o funcție care a tras pătratul interior și apoi o funcție recursivă care a făcut „pătrate” în jurul perimetrului pentru a include și exclude punctele rămase. Rezultatul a fost un rezultat incredibil de rapid - el a putut evalua milioane de puncte în microsecunde.

        Abordarea mea de mai sus este cu siguranță „brută”, dar capabilă. Multumesc din nou!

        • 23

          Doug,

          Am încercat să folosesc mysql și php pentru a evalua dacă un punct lat lat se află într-un poligon. Știți dacă prietenul dvs. dezvoltator a publicat exemple despre cum să realizați această sarcină. Sau cunoașteți exemple bune. Mulțumesc anticipat.

  16. 24

    Bună tuturor, aceasta este declarația mea SQL de testare:

    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

    și Mysql îmi spune că distanța nu există ca o coloană, pot folosi ordinea după, o pot face fără UNDE și funcționează, dar nu cu ea ...

  17. 26

    Acest lucru este grozav, cu toate acestea este la fel ca păsările zboară. Ar fi minunat să încercăm să încorporăm API-ul Google Maps într-un fel (poate folosind drumuri etc.) Doar să ne facem o idee folosind o altă formă de transport. Încă nu am făcut încă o funcție de recoacere simulată în PHP care ar putea oferi o soluție eficientă la problema vânzătorului călător. Dar cred că aș putea să refolosesc o parte din codul dvs. pentru a face acest lucru.

  18. 27
  19. 28

    Bun articol! Am găsit o mulțime de articole care descriu cum să calculez distanța dintre două puncte, dar căutam cu adevărat fragmentul SQL.

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

    2 zile de cercetare pentru a găsi în sfârșit această pagină care îmi rezolvă problema. Se pare că ar fi mai bine să-mi arunc WolframAlpha și să-mi dau seama de matematică. Schimbarea de la WHERE la HAVING are scriptul meu în stare de funcționare. MULȚUMESC

  25. 37
    • 38

      Mulțumesc Georgi. Am tot obținut „distanța” coloanei nu a fost găsită. Odată ce am schimbat WHERE to HAVING, a funcționat ca un farmec!

  26. 39

    Aș vrea să fie prima pagină pe care am găsit-o în acest sens. După ce am încercat multe comenzi diferite, aceasta a fost singura care a funcționat corect și cu modificările minime necesare pentru a se potrivi propriei mele baze de date.
    Multumesc mult!

  27. 40

    Aș vrea să fie prima pagină pe care am găsit-o în acest sens. După ce am încercat multe comenzi diferite, aceasta a fost singura care a funcționat corect și cu modificările minime necesare pentru a se potrivi propriei mele baze de date.
    Multumesc mult!

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

    Știu că această formulă funcționează, dar nu văd unde se ia în calcul raza pământului. Ma poate lumina cineva, te rog?

  34. 49
  35. 50
  36. 52

    Mulțumesc Douglas, interogarea SQL este exact ceea ce aveam nevoie și m-am gândit că va trebui să o scriu singură. M-ați salvat posibil de ore de curbă de învățare latitudine și longitudine!

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

    mulțumesc pentru postarea acestui articol util,  
    dar dintr-un anumit motiv aș vrea să întreb
    cum să obțineți distanța dintre coords din mysql db și coords inserate în php de către utilizator?
    pentru a descrie mai clar:
    1. Utilizatorul trebuie să introducă [id] pentru selectarea datelor specificate din db și din coordonatele utilizatorului
    2. fișierul php obține datele țintă (coords) folosind [id] și apoi calculează distanța dintre utilizator și punctul țintă

    sau pur și simplu puteți obține distanța de la codul de mai jos?

    $ qry = „SELECT *, (((acos (sin ((“. $ latitude. ”* pi () / 180)) * sin ((` Latitude` * pi () / 180)) + cos ((“. $ latitudine. ”* pi () / 180)) * cos ((` Latitudine` * pi () / 180)) * cos (((„. $ longitudine.” - „Longitudine”) * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) ca distanță DE LA `MyTable` WHERE distance> =“. $ Distance. ” >>>> pot „scoate” distanța de aici?
    multumesc din nou,
    Timmy S

  41. 60

    ok, tot ce am încercat nu funcționează. Adică, ceea ce am funcționează, dar distanțele sunt departe.

    Poate cineva să vadă ce este în neregulă cu acest cod?

    if (isset ($ _ POST ['prezentat']))) {$ z = $ _POST ['cod poștal']; $ r = $ _POST ['radius']; ecou „Rezultate pentru„. $ 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. city, 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 ") sau mor (mysql_error ()); while ($ row = mysql_fetch_array ($ sql)) {$ store1 = $ row ['MktName']. "”; $ magazin = $ rând ['LocAddSt']. ””; $ store. = $ row ['LocAddCity']. ",". $ row ['LocAddState']. " „. $ Rând [„ cod poștal ”]; $ latitude1 = $ rând ['lat']; $ longitudine1 = $ rând ['lon']; $ latitude2 = $ rând ['y1']; $ longitudine2 = $ rând ['x1']; $ oraș = $ rând ['oraș']; $ state = $ rând ['state']; $ dis = getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi'); // $ dis = distanță ($ lat1, $ lon1, $ lat2, $ lon2); $ Verified = $ rând ['Verified']; if ($ Verified == '1') {ecou „”; ecou „”. $ magazin. ””; ecou $ dis. „La o milă distanță”; ecou „”; } else {ecou „”. $ store. ””; ecou $ dis. „La o milă distanță”; ecou „”; }}}

    funcțiile mele.cod PHP
    function getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi') {$ theta = $ longitude1 - $ longitude2; $ distanță = (sin (deg2rad ($ latitude1)) * sin (deg2rad ($ latitude2))) + (cos (deg2rad ($ latitude1)) * cos (deg2rad ($ latitude2)) * cos (deg2rad ($ theta)) ); $ distanță = acos ($ distanță); $ distanță = rad2deg ($ distanță); $ distanță = $ distanță * 60 * 1.1515; switch ($ unit) {case 'Mi': break; cazul „Km”: $ distanță = $ distanță * 1.609344; } return (rundă ($ distanță, 2)); }

    Va multumesc anticipat

  42. 61
  43. 62

    Hei Douglas, articol grozav. Mi s-a părut interesantă explicația dvs. asupra conceptelor geografice și a codului. Singura mea sugestie ar fi spațierea și indentarea codului pentru afișare (cum ar fi Stackoverflow, de exemplu). Înțeleg că doriți să economisiți spațiu, dar spațierea / indentarea convențională a codurilor ar face mult mai ușor pentru mine, ca programator, să citesc și să disec. Oricum, asta este un lucru mic. Continuă lucrarea minunată.

  44. 64
  45. 65

    aici, în timp ce folosim cu funcția, obținem un tip de distanță .. în timp ce folosim interogarea viitoare, alt tip de distanță

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

    pare mai rapid (mysql 5.9) să folosiți de două ori formula în selectare și unde:
    $ formula = „((((acos (sin ((“. $ latitude. ”* pi () / 180)) * sin ((` Latitude` * pi () / 180)) + cos ((„. $ latitude. ”* Pi () / 180)) * cos ((` Latitudine` * pi () / 180)) * cos (((„. $ Longitudine.” - `Longitudine`) * pi () / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) ”;
    $ sql = 'SELECT *,'. $ formula. ' ca distanță DE la tabelul UNDE '.. $ formula.' <= '. $ distanță;

  51. 71
  52. 72

    Mulțumesc mult pentru forfecare acest articol.este foarte util.
    PHP a fost creat la început ca o simplă platformă de scripting numită „Pagina personală personală”. În prezent, PHP (prescurtarea pentru Hypertext Preprocessor) este o alternativă a tehnologiei Microsoft Active Server Pages (ASP).

    PHP este un limbaj open source pe partea de server care este utilizat pentru crearea de pagini web dinamice. Poate fi încorporat în HTML. PHP este de obicei utilizat împreună cu o bază de date MySQL pe serverele web Linux / UNIX. Este probabil cel mai popular limbaj de scriptare.

  53. 73

    Am găsit că soluția de mai sus nu funcționează corect.
    Trebuie să mă schimb în:

    $ qqq = „SELECT *, (((acos (sin ((“. $ latitude. ”* pi () / 180)) * sin ((` latt` * pi () / 180)) + cos ((”. $ latitudine. „* pi () / 180)) * cos ((` latt` * pi () / 180)) * cos (((”. $ longitudine.„ - `longt`) * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515) ca distanță DIN „registru“ “;

  54. 75
  55. 76

    Bună ziua, vă rog, voi avea nevoie de ajutorul vostru în această privință.

    Am făcut o cerere de primire către serverul meu web http://localhost:8000/users/findusers/53.47792/-2.23389/20/
    53.47792 = $ latitudine
    -2.23389 = $ longitudine
    și 20 = distanța pe care vreau să o recuperez

    Cu toate acestea, folosind formula dvs., recuperează toate rândurile din db-ul meu

    $ rezultate = 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) ca distanță DE la markeri AVÂND distanță> = „. $ Distanță));

    [{„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, „distanță”: 16079.175940152}, {„id”: 3, „nume”: „Kapp's Pizza Bar & Grill”, „adresă”: „191 Castro St, Mountain View, CA”, „lat”: 37.393886566162, ”Lng”: - 122.07891845703, ”distance”: 16078.381373826}, {“id”: 4, ”name”: ”Pizza de masă rotundă: 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 ”:” The bars and grills ”,” address ”:” 24 Whiteley Street, Manchester ”,” lat ”: 53.485118865967,” lng ”: - 2.1828699111938,” distance ”: 8038.7620112314}]

    Vreau să recuperez doar rânduri cu 20 de mile, dar aduce toate rândurile. Te rog ce fac greșit

Ce părere ai?

Acest site folosește Akismet pentru a reduce spamul. Aflați cum sunt procesate datele despre comentarii.