PHP i SQL: Izračunavanje ili ispitivanje udaljenosti velikog kruga između točaka zemljopisne širine i dužine pomoću Haversine formule

Formula Haversine - Izračunajte udaljenost velike kružnice pomoću PHP-a ili MySQL-a

Ovog sam mjeseca prilično programirao na PHP-u i MySQL-u s obzirom na GIS. Pregledavajući mrežu, zapravo sam teško pronašao neke od njih Geografski izračuni kako bih pronašao udaljenost između dva mjesta pa sam ih želio podijeliti ovdje.

Karta leta Europa s velikom kružnom udaljenostom

Jednostavan način izračuna udaljenosti između dviju točaka je pomoću pitagorejske formule za izračunavanje hipotenuze trokuta (A² + B² = C²). Ovo je poznato kao Euklidska udaljenost.

To je zanimljiv početak, ali se ne odnosi na Geografiju jer je udaljenost između crta zemljopisne širine i dužine ni jednaka udaljenost odvojeno. Kako se približavate ekvatoru, zemljopisne širine se sve više razdvajaju. Ako koristite neku jednostavnu jednadžbu triangulacije, ona može točno izmjeriti udaljenost na jednom mjestu, a na drugom strašno pogrešno, zbog zakrivljenosti Zemlje.

Udaljenost velikog kruga

Rute koje se prelaze velike udaljenosti oko Zemlje poznate su kao Udaljenost velikog kruga. To jest ... najkraća udaljenost između dviju točaka na kugli razlikuje se od točaka na ravnoj karti. Kombinirajte to s činjenicom da crte zemljopisne širine i dužine nisu jednako udaljene ... i imate težak izračun.

Evo fantastičnog video objašnjenja kako funkcioniraju Veliki krugovi.

Formula Haversine

Udaljenost koja koristi zakrivljenost Zemlje uključena je u Formula haversina, koji koristi trigonometriju kako bi omogućio zakrivljenost zemlje. Kada nalazite udaljenost između dva mjesta na zemlji (zračnom linijom), ravna crta je doista luk.

To je primjenjivo u zračnom letu - jeste li ikada pogledali stvarnu kartu letova i primijetili da su zaobljeni? To je zato što je kraće letjeti u luku između dvije točke nego izravno do mjesta.

PHP: Izračunajte udaljenost između 2 zemljopisne širine i dužine

U svakom slučaju, ovdje je PHP formula za izračunavanje udaljenosti između dviju točaka (zajedno s pretvaranjem milja u odnosu na kilometar) zaokruženo na dvije decimale.

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: Dohvaćanje svih zapisa unutar raspona izračunavanjem udaljenosti u miljama pomoću geografske širine i dužine

Također je moguće koristiti SQL za izračun kako biste pronašli sve zapise na određenoj udaljenosti. U ovom primjeru postavit ću upit MyTable u MySQL kako bih pronašao sve zapise koji su manji ili jednaki promjenljivoj $ distance (u miljama) do moje lokacije na $ latitude i $ longitude:

Upit za dohvaćanje svih zapisa unutar određenog udaljenost izračunavanjem udaljenosti u miljama između dvije točke geografske širine i dužine su:

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

Morat ćete prilagoditi ovo:

  • $ zemljopisne dužine - ovo je PHP varijabla gdje prolazim dužinu točke.
  • $ zemljopisne širine - ovo je PHP varijabla gdje prolazim dužinu točke.
  • $ udaljenost - ovo je udaljenost na kojoj biste željeli pronaći sve zapise manje ili jednake.
  • stol - ovo je tablica ... morat ćete je zamijeniti imenom svoje tablice.
  • širina - ovo je polje vaše geografske širine.
  • dužina - ovo je polje vaše zemljopisne dužine.

SQL: Dohvaćanje svih zapisa unutar raspona izračunavanjem udaljenosti u kilometrima pomoću zemljopisne širine i dužine

Evo i SQL upita pomoću kilometara u MySQL-u:

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

Morat ćete prilagoditi ovo:

  • $ zemljopisne dužine - ovo je PHP varijabla gdje prolazim dužinu točke.
  • $ zemljopisne širine - ovo je PHP varijabla gdje prolazim dužinu točke.
  • $ udaljenost - ovo je udaljenost na kojoj biste željeli pronaći sve zapise manje ili jednake.
  • stol - ovo je tablica ... morat ćete je zamijeniti imenom svoje tablice.
  • širina - ovo je polje vaše geografske širine.
  • dužina - ovo je polje vaše zemljopisne dužine.

Koristila sam ovaj kod u platformi za mapiranje poduzeća koju smo koristili za maloprodajnu trgovinu s više od 1,000 lokacija širom Sjeverne Amerike i djelovala je lijepo.

76 Komentari

  1. 1

    Puno vam hvala što ste podijelili. Ovo je bio jednostavan posao kopiranja i lijepljenja i izvrsno funkcionira. Uštedjeli ste mi puno vremena.
    Upozorenje za sve koji prenose na C:
    dvostruki stupanj 2rad (dvostruki stupanj) {povratni stupanj * (3.14159265358979323846 / 180.0); }

  2. 2

    Vrlo lijep članak - izvrsno je funkcionirao - morao sam samo promijeniti naziv stola koji drži lat-long. Djeluje prilično brzo na .. Imam relativno mali broj lat-long-a (<400), ali mislim da bi ovo bilo lijepo. I lijepo mjesto - upravo sam ga dodao na svoj račun na del.icio.us i redovito ću provjeravati.

  3. 4
  4. 5
  5. 8

    mislim da vašem SQL-u treba izjava o posjedovanju.
    umjesto WHERE udaljenost <= $ udaljenost koju ćete možda trebati
    koristite HAVING distance <= $ distance

    inače hvala što ste mi uštedjeli hrpu vremena i energije.

  6. 10
  7. 11
  8. 12

    Hvala vam što ste podijelili ovaj kod. Uštedjelo mi je puno vremena za razvoj. Također, hvala čitateljima što su istakli da je HAVING izjava neophodna za MySQL 5.x. Vrlo korisno.

  9. 14
  10. 15

    Pozdrav,

    Drugo pitanje. Postoji li formula za NMEA nizove poput one u nastavku?

    1342.7500, N, 10052.2287, E

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

    Hvala,
    Pljačkati

  11. 16

    Također sam otkrio da WHERE nije radio za mene. Promijenio je u HAVING i sve funkcionira savršeno. Isprva nisam pročitao komentare i prepisao ih pomoću ugniježđenog odabira. Obje će raditi sasvim u redu.

  12. 17
  13. 18

    Nevjerojatno korisno, puno hvala! Imao sam nekih problema s novim "HAVING", umjesto s "WHERE", ali kad sam ovdje pročitao komentare (nakon otprilike pola sata frustriranog škrgutanja zubima = P), počeo sam lijepo raditi. Hvala ^ _ ^

  14. 19
  15. 20

    Imajte na umu da će takva odabrana izjava biti vrlo računalno intenzivna, a time i spora. Ako imate puno tih upita, to može prilično brzo zabrljati.

    Puno manje intenzivan pristup je pokretanje prvog (sirovog) odabira pomoću KVADRATNOG područja definiranog izračunatom udaljenostom, tj. "Odaberi * iz naziva tablice gdje je geografska širina između lat1 i lat2 i dužina između lon1 i lon2". lat1 = targetlatitude - latdiff, lat2 = targetlatitude + latdiff, slično s lon. latdiff ~ = udaljenost / 111 (za km) ili udaljenost / 69 za milje jer je 1 stupanj zemljopisne širine ~ 111 km (mala varijacija jer je zemlja blago ovalna, ali dovoljna za ovu svrhu). londiff = udaljenost / (abs (cos (deg2rad (zemljopisna širina)) * 111)) - ili 69 milja (zapravo možete uzeti malo veći kvadrat kako biste uzeli u obzir varijacije). Zatim uzmite rezultat i unesite ga u radijalni odabir. Samo ne zaboravite uzeti u obzir koordinate izvan granica - tj. Raspon prihvatljive dužine je -180 do +180, a raspon prihvatljive zemljopisne širine -90 do +90 - u slučaju da vaš latdiff ili londiff radi izvan tog raspona . Imajte na umu da u većini slučajeva to možda nije primjenjivo jer utječe samo na izračune preko crte kroz Tihi ocean od pola do pola, iako presijeca dio Čukotke i dio Aljaske.

    Ono što time postižemo je značajno smanjenje broja bodova na osnovu kojih izračunate. Ako imate milijun globalnih točaka u bazi podataka raspoređenih otprilike ravnomjerno i želite pretraživati ​​u krugu od 100 km, tada je vaše prvo (brzo) pretraživanje površine 10000 kvadratnih kilometara i vjerojatno će dati oko 20 rezultata (na temelju ravnomjerne raspodjele po površine oko 500 milijuna kvadratnih kilometara), što znači da složeni izračun udaljenosti za ovaj upit izvodite 20 puta, umjesto milijun puta.

    • 21
      • 22

        Fantastičan savjet! Zapravo sam surađivao s programerom koji je napisao funkciju koja je povukla unutarnji kvadrat, a zatim rekurzivnu funkciju koja je napravila 'kvadrate' po obodu kako bi uključila i izuzela preostale točke. Rezultat je bio nevjerojatno brz - mogao je procijeniti milijune bodova u mikrosekundama.

        Moj gornji pristup je definitivno 'grub', ali sposoban. Hvala još jednom!

        • 23

          Doug,

          Pokušavao sam pomoću mysqla i php-a procijeniti nalazi li se duga lat točka unutar poligona. Znate li je li vaš prijatelj programer objavio primjere kako izvršiti ovaj zadatak. Ili znate neke dobre primjere. Hvala unaprijed.

  16. 24

    Pozdrav svima, ovo je moja testna SQL izjava:

    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 govori da ta udaljenost ne postoji kao stupac, mogu koristiti poredak po, mogu to učiniti bez WHERE, i to funkcionira, ali ne s tim ...

  17. 26

    To je sjajno, ali baš kao što ptice lete. Bilo bi sjajno pokušati na ovaj način nekako integrirati API za Google Maps (možda koristeći ceste itd.) Samo da biste dali ideju koristeći drugi oblik prijevoza. Još uvijek nisam izradio simuliranu funkciju žarenja u PHP-u koja bi mogla ponuditi učinkovito rješenje problema putničkog trgovca. Ali mislim da bih možda mogao ponovno iskoristiti neki vaš kôd za to.

  18. 27
  19. 28

    Dobar članak! Pronašao sam puno članaka koji opisuju kako izračunati udaljenost između dvije točke, ali stvarno sam tražio SQL isječak.

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

    2 dana istraživanja kako bih napokon pronašao ovu stranicu koja rješava moj problem. Čini se da je bolje da uništim WolframAlphu i počnem računati. Promjena iz WHERE u HAVING ima moju skriptu u ispravnom stanju. HVALA VAM

  25. 37
    • 38

      Hvala Georgi. Stalno sam dobivao stupac 'distance' koji nije pronađen. Jednom kada promijenim WHERE u HAVING to je djelovalo poput šarma!

  26. 39

    Volio bih da je ovo prva stranica koju sam našao na ovome. Nakon isprobavanja različitih naredbi, jedina je radila ispravno i uz minimalne promjene potrebne da uklopim vlastitu bazu podataka.
    Puno hvala!

  27. 40

    Volio bih da je ovo prva stranica koju sam našao na ovome. Nakon isprobavanja različitih naredbi, jedina je radila ispravno i uz minimalne promjene potrebne da uklopim vlastitu bazu podataka.
    Puno hvala!

  28. 41
  29. 42
  30. 43
  31. 45
  32. 46
  33. 47
  34. 49
  35. 50
  36. 52
  37. 53
  38. 55
  39. 56
  40. 58

    hvala što ste objavili ovaj koristan članak,  
    ali iz nekog bih razloga želio pitati
    kako dobiti udaljenost između koorda unutar mysql db i koorda koje je korisnik umetnuo u php?
    za jasnije opisivanje:
    1. korisnik mora umetnuti [id] za odabir određenih podataka iz db-a i samih korisničkih koordinata
    2. php datoteka dobiva ciljne podatke (koordine) pomoću [id] i zatim izračunava udaljenost između korisnika i ciljne točke

    ili možete jednostavno dobiti udaljenost od donjeg koda?

    $ qry = "ODABERI *, (((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) kao udaljenost OD `MyTable` WHERE udaljenost> =". $ Distance. " >>>> mogu li odavde "izvaditi" udaljenost?
    hvala još jednom,
    Timmy S

  41. 60

    ok, sve što sam pokušao ne radi. Mislim, ono što imam djeluje, ali udaljenosti su jako daleko.

    Može li itko vidjeti što nije u redu s ovim kodom?

    if (isset ($ _ POST ['submit'])) {$ z = $ _POST ['poštanski broj']; $ r = $ _POST ['radijus']; odjek „Rezultati za“. $ z; $ sql = mysql_query (“ODABERITE DISTINCT m.zipcode, m.MktName, m.LocAddSt, m.LocAddCity, m.LocAddState, m.x1, m.y1, m.verified, z1.lat, z2.lon, z1. grad, z1.država OD mrk m, zip z1, zip z2 GDJE m.zipcode = z1.zipcode I z2.zipcode = $ z AND (3963 * acos (skraćivanje (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 ") ili umri (mysql_error ()); while ($ row = mysql_fetch_array ($ sql)) {$ store1 = $ row ['MktName']. ""; $ store = $ row ['LocAddSt']. ""; $ store. = $ row ['LocAddCity']. ",". $ row ['LocAddState']. " ". $ Row ['poštanski broj']; $ latitude1 = $ row ['lat']; $ longitude1 = $ row ['lon']; $ latitude2 = $ row ['y1']; $ longitude2 = $ row ['x1']; $ city = $ row ['grad']; $ state = $ row ['state']; $ dis = getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi'); // $ dis = udaljenost ($ lat1, $ lon1, $ lat2, $ lon2); $ provjereno = $ redak ['provjereno']; if ($ provjereno == '1') {echo “”; echo "". $ store. ""; odjek $ dis. " kilometrima daleko"; jeka ""; } else {echo "". $ store. ""; odjek $ dis. " kilometrima daleko"; jeka ""; }}}

    moje funkcije.php kod
    funkcija getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi') {$ theta = $ longitude1 - $ longitude2; $ udaljenost = (sin (deg2rad ($ latitude1)) * * sin (deg2rad ($ latitude2))) + (cos (deg2rad ($ latitude1)) * * cos (deg2rad ($ latitude2)) * cos (deg2rad ($ theta)) ); $ udaljenost = acos ($ udaljenost); $ udaljenost = rad2deg ($ udaljenost); $ udaljenost = $ udaljenost * 60 * 1.1515; prekidač ($ jedinica) {slučaj 'Mi': prekid; slučaj 'Km': $ udaljenost = $ udaljenost * 1.609344; } povratak (krug ($ udaljenost, 2)); }

    Hvala unaprijed

  42. 61
  43. 62

    Hej Douglas, sjajan članak. Smatram da je vaše objašnjenje geografskih pojmova i koda zaista zanimljivo. Moj jedini prijedlog bio bi razmak i uvlačenje koda za prikaz (poput Stackoverflow-a, na primjer). Razumijem da želite uštedjeti prostor, ali uobičajeni razmak / uvlačenje koda olakšao bi mi čitanje i seciranje kao programera. U svakom slučaju, to je mala stvar. Nastavite tako s velikim radom.

  44. 64
  45. 65
  46. 66
  47. 67
  48. 68
  49. 69
  50. 70

    čini se bržim (mysql 5.9) upotrijebiti dvostruku formulu u odabiru i gdje:
    $ 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. ' kao udaljenost OD tablice GDJE '.. $ formula.' <= '. $ udaljenost;

  51. 71
  52. 72

    Puno hvala na posijecanju ovog članka. Vrlo je korisno.
    PHP je isprva stvoren kao jednostavna skriptna platforma nazvana "Osobna početna stranica". U današnje vrijeme PHP (skraćenica od Hypertext Preprocessor) alternativa je Microsoftovoj tehnologiji Active Server Pages (ASP).

    PHP je jezik otvorenog koda na poslužitelju koji se koristi za stvaranje dinamičnih web stranica. Može se ugraditi u HTML. PHP se obično koristi zajedno s MySQL bazom podataka na Linux / UNIX web poslužiteljima. To je vjerojatno najpopularniji skriptni jezik.

  53. 73

    Našao sam da gornje rješenje ne radi ispravno.
    Moram promijeniti na:

    $ qqq = "ODABERI *, (((acos (sin ((". $ latitude. "* pi () / 180)) * sin ((` latt` * pi () / 180)) + cos ((". $ zemljopisna širina. "* pi () / 180)) * cos ((` latt` * pi () / 180)) * cos (((". $ longitude." - `longt`) * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515) kao udaljenost OD `registra`“;

  54. 75
  55. 76

    Pozdrav, molim vas, zaista će mi trebati vaša pomoć oko toga.

    Zatražio sam zahtjev za svoj web-poslužitelj http://localhost:8000/users/findusers/53.47792/-2.23389/20/
    53.47792 = zemljopisna širina
    -2.23389 = zemljopisna dužina
    i 20 = udaljenost koju želim dohvatiti

    Međutim, koristeći vašu formulu, on dohvaća sve retke u mom db-u

    $ rezultati = DB :: select (DB :: raw (“SELECT *, (((acos (sin ((“. $ latitude. ”* pi () / 180)) * sin ((lat * pi () / 180 ))) + cos ((". $ latitude." * pi () / 180)) * cos ((lat * pi () / 180)) * cos (((". $ longitude." - lng) * pi ( ) / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) kao udaljenost OD markera KOJI IMAJU udaljenost> = “. $ Udaljenost));

    [{"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”: „Pizzeria Amici's East Coast”, „address”: „790 Castro St, Mountain View, CA”, „lat”: 37.387138366699, „lng”: -122.08323669434, "udaljenost": 16079.175940152}, {"id": 3, "ime": "Kapp's Pizza Bar & Grill", "adresa": "191 Castro St, Mountain View, CA", "lat": 37.393886566162, ”Lng”: - 122.07891845703, „udaljenost”: 16078.381373826}, {„id”: 4, „name”: „Pizza okruglog stola: 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 Pogled, CA "," lat ": 37.394012451172," lng ": - 122.09552764893," distance ": 16078.563225154}, {" id ": 6," name ":" Oreganova drvena pizza "," adresa ":" 4546 El Camino Real, Los Altos, CA "," lat ": 37.401725769043," lng ": - 122.11464691162," distance ": 16077.937560795}, {" id ”: 7,” name ”:” Barovi i gradele ”,“ adresa ”:” 24 Whiteley Street, Manchester ”,” lat ”: 53.485118865967,” lng ”: - 2.1828699111938,” distance ”: 8038.7620112314}]

    Želim dohvatiti samo retke s 20 kilometara, ali donosi sve redove. Molim te, što radim krivo

Što vi mislite?

Ova web stranica koristi Akismet za smanjenje neželjene pošte. Saznajte kako se podaci vašeg komentara obrađuju.