PHP ja SQL: arvutage või küsige suure ringi kaugust laius- ja pikkuskraadide vahel Haversine'i valemiga

Haversine'i valem - arvutage PHP või MySQL abil suur ringi kaugus

Sel kuul olen GIS-i osas üsna palju PHP-s ja MySQL-is programmeerinud. Netis ringi nuhkides oli mul tegelikult raske mõnda neist leida Geograafilised arvutused kahe asukoha vahelise kauguse leidmiseks tahtsin neid siin jagada.

Suure ringkaugusega Euroopa lennukaart

Lihtne viis kahe punkti vahelise kauguse arvutamiseks on Pythagorase valemi abil kolmnurga hüpotenuusi arvutamine (A² + B² = C²). Seda tuntakse kui Eukleidese kaugus.

See on huvitav algus, kuid see ei kehti geograafia kohta, kuna laius- ja pikkuskraadi joonte vaheline kaugus on mitte võrdne kaugus lahus. Ekvaatorile lähemale jõudes lähevad laiuskraadid üksteisest kaugemale. Kui kasutate mingisugust lihtsat triangulatsioonivõrrandit, võib see Maa kumeruse tõttu mõõta ühes kohas kaugust täpselt ja teises kohutavalt valesti.

Suur ringkaugus

Maad mööda pikki vahemaid läbitud marsruute nimetatakse Suur ringkaugus. See tähendab, et sfääri kahe punkti lühim kaugus erineb lameda kaardi punktidest. Kombineerige see asjaoluga, et laius- ja pikkuskraadid ei ole võrdsel kaugusel ... ja teil on keeruline arvutus.

Siin on fantastiline video selgitus suurepäraste ringide toimimise kohta.

Haversine valem

Maa kumerust kasutav kaugus on integreeritud Haversine valem, mis kasutab trigonomeetriat, et võimaldada maa kumerust. Kui leiate maa peal kahe koha vahemaa (linnulennul), on sirgjoon tõesti kaar.

See kehtib õhulennu puhul - kas olete kunagi tegelikku lennukaarti vaadanud ja märganud, et see on kaardus? Seda seetõttu, et kahe punkti vahelises kaares on lühem lennata kui otse asukohta.

PHP: arvutage kaugus kahe laius- ja pikkuskraadi vahel

Igatahes, siin on PHP valem kahe punkti vahelise kauguse arvutamiseks (koos teisendusega Miil vs Kilomeeter) ümardatuna kahe kümnendkohani.

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: kõigi vahemikus olevate kirjete hankimine, arvutades kauguse miilides laius- ja pikkuskraadi abil

Samuti on SQL-i abil võimalik teha arvutus, et leida kõik kirjed kindla vahemaa tagant. Selles näites küsin MySQL-is MyTable-ist, et leida kõik kirjed, mis on väiksemad või võrdsed muutujaga $ distance (miilides) minu asukohani $ latitude ja $ longitude:

Päring kõigi konkreetses olevate kirjete hankimiseks kaugus arvutades kahe laius- ja pikkuskraadi vahelise kauguse miilides, on:

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

Peate seda kohandama:

  • $ pikkuskraad - see on PHP muutuja, kus ma läbin punkti pikkuskraadi.
  • $ laiuskraad - see on PHP muutuja, kus ma läbin punkti pikkuskraadi.
  • $ vahemaa - see on vahemaa, mille soovite leida kõik kirjed vähem või võrdsed.
  • tabel - see on tabel ... soovite selle asendada oma tabeli nimega.
  • laius - see on teie laiuskraadi väli.
  • pikkuskraad - see on teie pikkuskraadi väli.

SQL: kõigi vahemikus olevate kirjete hankimine, arvutades pikkuse ja laiuse abil kilomeetrite pikkuse

Ja siin on SQL-päring, kasutades MySQL-is kilomeetreid:

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

Peate seda kohandama:

  • $ pikkuskraad - see on PHP muutuja, kus ma läbin punkti pikkuskraadi.
  • $ laiuskraad - see on PHP muutuja, kus ma läbin punkti pikkuskraadi.
  • $ vahemaa - see on vahemaa, mille soovite leida kõik kirjed vähem või võrdsed.
  • tabel - see on tabel ... soovite selle asendada oma tabeli nimega.
  • laius - see on teie laiuskraadi väli.
  • pikkuskraad - see on teie pikkuskraadi väli.

Kasutasin seda koodi ettevõtte kaardistamise platvormil, mida kasutasime Põhja-Ameerikas üle 1,000 asukohaga jaekaupluses ja see töötas suurepäraselt.

76 Kommentaarid

  1. 1

    Suur aitäh jagamise eest. See oli lihtne kopeerimis- ja kleepimistöö ning töötab suurepäraselt. Olete säästnud mind palju aega.
    FYI kõigile C-sse porteerivatele inimestele:
    kahekordne deg2rad (kahekordne kraad) {tagastuskraad * (3.14159265358979323846 / 180.0); }

  2. 2

    Väga kena postitustükk - töötas väga kenasti - pidin ainult muutma lat-longi hoidva laua nime. See töötab üsna kiiresti .. Mul on suhteliselt vähe lat-longe (<400), kuid ma arvan, et see skaalal kenasti. Ka tore sait - lisasin selle just oma del.icio.us kontole ja kontrollin regulaarselt uuesti.

  3. 4
  4. 5
  5. 8

    ma arvan, et teie SQL vajab omavat lauset.
    selle asemel, et teil oleks vaja WHERE-kaugust <= $ -vahemaa
    kasuta HAVING distance <= $ distance

    muidu aitäh, et säästsite mulle hulga aega ja energiat.

  6. 10
  7. 11
  8. 12

    Suur aitäh selle koodi jagamise eest. See säästis mulle palju arendusaega. Samuti aitäh oma lugejatele, kes juhtisid tähelepanu sellele, et MySQL 5.x jaoks on vajalik HAVING-lause. Väga abivalmis.

  9. 14
  10. 15

    Tere,

    Järgmine küsimus. Kas on olemas NMEA stringide valem, nagu allpool?

    1342.7500, N, 10052.2287, E

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

    Thanks,
    Röövima

  11. 16

    Samuti leidsin, et WHERE ei töötanud minu jaoks. Muutis selle HAVING-iks ja kõik töötab ideaalselt. Alguses ei lugenud ma kommentaare ja kirjutasin need ümber pesastatud valiku abil. Mõlemad toimivad suurepäraselt.

  12. 17

    Tänan teid väga, et olete mysql-i kirjutatud skripti eest pidanud tegema, pidin lihtsalt mõned väiksemad muudatused tegema (HAVING) 🙂
    Saage tööd

  13. 18

    Uskumatult abivalmis, suur aitäh! Mul oli uue "HAVING" -iga pigem probleeme, mitte "WHERE" -ga, kuid kui ma siin kommentaare lugesin (pärast umbes pooletunnist pettumuseks hammaste krigistamist = P), sain selle kenasti tööle. Aitäh ^ _ ^

  14. 19
  15. 20

    Pidage meeles, et selline valitud lause on arvutuslikult väga intensiivne ja seetõttu aeglane. Kui teil on palju neid päringuid, võib see üsna kiiresti takerduda.

    Palju vähem intensiivne lähenemine on esimese (töötlemata) valiku käivitamine, kasutades ruutu ala, mis on määratud arvutatud kaugusega, st “vali * tabenime alt, kus laiuskraad lat1 ja lat2 vahel ning pikkus lon1 ja lon2 vahel”. lat1 = targetlatitude - latdiff, lat2 = targetlatitude + latdiff, sarnane loniga. latdiff ~ = kaugus / 111 (km kohta) või kaugus / 69 miili eest, kuna üks laiuskraad on ~ 1 km (väike varieeruvus, kuna maa on kergelt ovaalne, kuid selleks piisav). londiff = kaugus / (abs (cos (deg111rad (laiuskraad)) * 2)) - või 111 miili (variatsioonide arvestamiseks võite tegelikult võtta veidi suurema ruudu). Seejärel võtke selle tulemus ja sisestage see radiaalvalikusse. Ärge unustage arvestada piiriüleste koordinaatidega - st vastuvõetava pikkuskraadi vahemik on -69 kuni +180 ja vastuvõetava laiuskraadi vahemik on -180 kuni +90 - juhul, kui teie latdiff või londiff jookseb väljaspool seda vahemikku . Pange tähele, et enamikul juhtudel ei pruugi see olla kohaldatav, kuna see mõjutab ainult Vaikse ookeani poolusest pooluseni kulgeva joone ületavaid arvutusi, kuigi see lõikub osa tšukotkast ja osa alaskast.

    Selle abil saavutame märkimisväärse arvu punktide arvu, mille põhjal selle arvutuse teete. Kui teil on andmebaasis miljon globaalset punkti, mis on jaotatud ligikaudu ühtlaselt ja soovite otsida 100 km raadiuses, on teie esimene (kiire) otsing pindala 10000 ruutkilomeetrit ja annab tõenäoliselt umbes 20 tulemust (põhineb ühtlasel jaotusel pindala on umbes 500 miljonit ruutkilomeetrit), mis tähendab, et käivitate selle päringu jaoks kompleksse kauguse arvutamise 20 korra, mitte miljoni korra.

    • 21
      • 22

        Fantastiline nõu! Ma töötasin tegelikult koos arendajaga, kes kirjutas funktsiooni, mis tõmbas sisemise ruudu ja seejärel rekursiivse funktsiooni, mis muutis perimeetri ümber ruudud, et ülejäänud punktid lisada ja välja jätta. Tulemuseks oli uskumatult kiire tulemus - ta oskas mikrosekundites hinnata miljoneid punkte.

        Minu ülaltoodud lähenemine on kindlasti "toores", kuid võimekas. Aitäh veel kord!

        • 23

          Doug,

          Olen püüdnud kasutada mysqli ja php-d, et hinnata, kas lati pikk punkt on hulknurga sees. Kas teate, kas teie arendajast sõber avaldas näiteid selle ülesande täitmise kohta. Või teate häid näiteid. Ette tänades.

  16. 24

    Tere kõigile, see on minu test SQL-lause:

    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

    ja Mysql ütleb mulle, et vahemaa pole veeruna olemas, võin kasutada järjekorda, saan hakkama ka ilma KUHU ja see töötab, aga mitte sellega ...

  17. 26

    See on suurepärane, aga just nii, nagu linnud lendavad. Tore oleks proovida sellesse kuidagi lisada google maps API (võib-olla kasutada teid jne), lihtsalt selleks, et anda mõni muu transpordiliigi abil idee. Mul pole veel PHP-s simuleeritud lõõmutamisfunktsiooni teha, mis suudaks pakkuda tõhusat lahendust rändava müügimehe probleemile. Kuid arvan, et võin selleks mõnda teie koodi taaskasutada.

  18. 27
  19. 28

    Hea artikkel! Leidsin palju artikleid, mis kirjeldavad kahe punkti vahelise kauguse arvutamist, kuid otsisin tõesti SQL-i fragmenti.

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

    2 päeva uurimistööd, et lõpuks leida see leht, mis minu probleemi lahendab. Tundub, et ma parem lööksin oma WolframAlpha välja ja täiendaksin matemaatikat. Üleminekul WHERE-lt HAVING-ile on minu skript töökorras. AITÄH

  25. 37
  26. 39

    Soovin, et see oleks esimene leht, mille selle kohta leidsin. Pärast paljude erinevate käskude proovimist töötas see ainsana korralikult ja minu andmebaasi mahtumiseks oli vaja teha minimaalseid muudatusi.
    Suured tänud!

  27. 40

    Soovin, et see oleks esimene leht, mille selle kohta leidsin. Pärast paljude erinevate käskude proovimist töötas see ainsana korralikult ja minu andmebaasi mahtumiseks oli vaja teha minimaalseid muudatusi.
    Suured tänud!

  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

    aitäh selle kasuliku artikli postitamise eest,  
    aga millegipärast tahaksin küsida
    kuidas saada kaugus mysql db-i sees olevate koosluste ja kasutaja poolt php-le sisestatud koordide vahel?
    kirjeldada selgemalt:
    1. kasutaja peab sisestama [id] määratud andmete valimiseks db-st ja kasutaja enda koordinaatidest
    2. php-fail saab sihtandmed (koordinaadid), kasutades [id], ja arvutab seejärel kasutaja ja sihtpunkti vahe

    või saab lihtsalt lihtsalt kauguse allolevast koodist?

    $ qry = “SELECT *, (((acos (sin ((“. $ laiuskraad. ”* pi () / 180)) * sin ((“ Laiuskraad * * pi () / 180)] + cos ((“. $ laiuskraad. "* pi () / 180)) * cos ((" Laiuskraad "* pi () / 180)) * cos (((". $ pikkuskraad. "-" Pikkuskraad ") * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) kaugusena MyTable'ist WHERE kaugus> = ". $ Kaugus." >>>> kas ma saan siit kauguse välja võtta?
    aitäh veel kord,
    Timmy S

  41. 60

    ok, kõik, mida olen proovinud, ei toimi. Ma mõtlen, et see, mis mul on, töötab, kuid vahemaad on kaugel.

    Kas keegi võiks näha, mis sellel koodil viga on?

    if (isset ($ _ POST ['esitatud'])) {$ z = $ _POST ['sihtnumber']; $ r = $ _POST ['raadius']; kaja "Tulemused päringule". $ 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. linn, z1.riik mrk m, zip z1, zip z2 KUS 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 ") või surevad (mysql_error ()); while ($ row = mysql_fetch_array ($ sql)) {$ store1 = $ row ['MktName']. ""; $ pood = $ rida ['LocAddSt']. ""; $ store. = $ row ['LocAddCity']. ",". $ row ['LocAddState']. " ". $ Rida ['sihtnumber']; $ latitude1 = $ rida ['lat']; $ pikkuskraad1 = $ rida ['lon']; $ latitude2 = $ rida ['y1']; $ pikkuskraad2 = $ rida ['x1']; $ linn = $ rida ['linn']; $ osariik = $ rida ['osariik']; $ dis = getnew ($ laiuskraad1, $ pikkuskraad1, $ laiuskraad2, $ pikkuskraad2, $ ühik = 'Mi'); // $ dis = kaugus ($ lat1, $ lon1, $ lat2, $ lon2); $ verified = $ row ['kinnitatud']; if ($ verified == '1') {kaja ""; kaja “”. $ pood. ””; kaja $ dis. " miile eemal"; kaja “”; } else {kaja "". $ pood. ""; kaja $ dis. " miile eemal"; kaja “”; }}}

    minu funktsioonid.php kood
    funktsioon getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi') {$ theta = $ longitude1 - $ longitude2; $ kaugus = (sin (deg2rad ($ laiuskraad1)) * sin (deg2rad ($ laiuskraad2))) + (cos (deg2rad ($ laiuskraad1)) * cos (deg2rad ($ laiuskraad2)) * cos (deg2rad ($ teeta)) ); $ kaugus = acos ($ kaugus); $ kaugus = rad2deg ($ kaugus); $ kaugus = $ kaugus * 60 * 1.1515; switch ($ unit) {juhtum 'Mi': break; juhtum 'Km': $ kaugus = $ kaugus * 1.609344; } tagasipöördumine (ring ($ vahemaa, 2)); }

    Tänan ette

  42. 61
  43. 62

    Hei Douglas, suurepärane artikkel. Mulle tundus teie geograafiliste mõistete ja koodi selgitus tõesti huvitav. Minu ainus soovitus oleks tühik ja taandada kuvatav kood (näiteks Stackoverflow). Ma saan aru, et soovite ruumi kokku hoida, kuid tavapärane koodivahe / taane muudaks minu kui programmeerija lugemise ja lahkamise palju lihtsamaks. Igatahes on see väike asi. Jätkake suurepärast tööd.

  44. 64
  45. 65

    siin koos funktsiooniga kasutamisel saame ühte tüüpi vahemaad ... samas kui kasutame päringut selle tuleva muud tüüpi vahemaa jaoks

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

    tundub kiirem (mysql 5.9) kasutada valemis kaks korda valikut ja kus:
    $ valem = “(((acos (sin ((“. $ laiuskraad. ”* pi () / 180)) * sin ((“ Laiuskraad ”* pi () / 180)) + cos ((“. $ laiuskraad. ”* Pi () / 180)) * cos ((" Laiuskraad "* pi () / 180)) * cos (((". $ Pikkuskraad. "-" Pikkuskraad ") * pi () / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) ”;
    $ sql = 'SELECT *,'. $ valem. ' kui kaugus tabelist WHERE '.. $ valem.' <= '. $ kaugus;

  51. 71
  52. 72

    Suur tänu selle artikli lõikamise eest. See on väga kasulik.
    PHP loodi algul lihtsa skriptimisplatvormina, mille nimi oli “Personal Home Page”. Tänapäeval on PHP (Hypertext Preprocessori lühend) alternatiiv Microsofti Active Server Pages (ASP) tehnoloogiale.

    PHP on avatud lähtekoodiga serveripoolne keel, mida kasutatakse dünaamiliste veebilehtede loomiseks. Seda saab manustada HTML-i. PHP-d kasutatakse tavaliselt koos MySQL-i andmebaasiga Linux / UNIX veebiserverites. See on ilmselt kõige populaarsem skriptikeel.

  53. 73

    Leidsin, et ülaltoodud lahendus ei tööta korralikult.
    Pean üle minema:

    $ qqq = “SELECT *, (((acos (sin ((“. $ laiuskraad. ”* pi () / 180)) * sin ((` latt` * pi () / 180)) + cos ((". $ laiuskraad. “* pi () / 180)) * cos ((" latt "* pi () / 180)) * cos (((" ". $ pikkuskraad." - "longt") * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515) kaugus registrist “;

  54. 75

    aitäh härra täiuslikult ... aga mul on üks küsimus, kui ma tahan väljundit ilma komakohata esitada, siis mida ma teha saan?

    Tänud ette.

  55. 76

    Tere, palun mul selles osas tõesti teie abi.

    Tegin oma veebiserverisse päringu http://localhost:8000/users/findusers/53.47792/-2.23389/20/
    53.47792 = $ laiuskraad
    -2.23389 = $ pikkuskraad
    ja 20 = vahemaa, mida tahan kätte saada

    Kuid teie valemit kasutades otsib see kõik minu DB read

    $ results = DB :: select (DB :: raw (“SELECT *, ((acos (sin ((“ “$ latitude.” * pi () / 180)) * sin ((lat * pi () / 180 )) + cos ((“. $ laiuskraad.” * pi () / 180)) * cos ((lat * pi () / 180)) * cos (((“. $ pikkuskraad.” - lng) * pi ( ) / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) kaugusena markeritest, millel on kaugus> = “. $ Kaugus));

    [{"Id": 1, "nimi": "Frankie Johnnie ja Luigo liiga", "aadress": "939 W El Camino Real, Mountain View, CA", "lat": 37.386337280273, "lng": - 122.08582305908, ”Distance”: 16079.294719663}, {“id”: 2, ”name”: “Amici idaranniku pitsarestoran”, ”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”: “Round Table Pizza: 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 Vaade, CA ”,” lat ”: 37.394012451172,” lng ”: - 122.09552764893,“ kaugus ”: 16078.563225154}, {“ id ”: 6,“ nimi ”:“ Oregano puuküttega pizza ”,” aadress ”:” 4546 El Camino Real, Los Altos, CA ”,” lat ”: 37.401725769043,” lng ”: - 122.11464691162,“ kaugus ”: 16077.937560795}, {“ id ”: 7,” name ”:” Baarid ja grillid ”,” aadress ”:” 24 Whiteley Street, Manchester ”,” lat ”: 53.485118865967,” lng ”: - 2.1828699111938,” distance ”: 8038.7620112314}]

    Ma tahan hankida vaid 20 miiliga read, kuid see toob kõik read. Palun, mida ma valesti teen

Mis sa arvad?

Sellel saidil kasutatakse rämpsposti vähendamiseks Akismetit. Vaadake, kuidas teie andmeid töödeldakse.