PHP en SQL: Berekkenje of freegje grutte sirkelôfstân tusken punten fan breedte en lingtegraad mei de Haversine-formule

Haversine Formule - Berekkenje Grutte sirkelôfstân mei PHP as MySQL
Lêzetiid: 3 minuten

Dizze moanne haw ik frijwat programmearre yn PHP en MySQL oangeande GIS. Snoekjend om it net, ik hie eins in hurde tiid om wat fan te finen Geografyske berekkeningen de ôfstân te finen tusken twa lokaasjes, dus ik woe se hjir diele.

Fleankaart Jeropa Mei Grutte Sirkelôfstân

De ienfâldige manier om in ôfstân te berekkenjen tusken twa punten is mei de Pythagoreaske formule om de hypotenuse fan in trijehoek te berekkenjen (A² + B² = C²). Dit wurdt bekend as de Euklidyske ôfstân.

Dat is in nijsgjirrige start, mar it is net fan tapassing op Geografy, om't de ôfstân tusken rigels fan breedte en lingtegraad is net in gelikense ôfstân apart. As jo ​​tichter by de evener komme, komme breedtegraad fierder útinoar. As jo ​​in soarte fan ienfâldige triangulaasje-fergeliking brûke, kin it ôfstân krekts mjitte op 'e iene lokaasje en ferskriklik ferkeard yn' e oare, fanwegen de kromming fan 'e Ierde.

Grutte sirkelôfstân

De rûtes dy't lange ôfstannen om 'e ierde wurde reizge steane bekend as de Grutte sirkelôfstân, Dat is ... de koartste ôfstân tusken twa punten op in sfear is oars as de punten yn in platte kaart. Kombinearje dat mei it feit dat de breedte- en lingtegraadlinen net lykop binne ... en jo hawwe in drege berekkening.

Hjir is in fantastyske fideo-útlis oer hoe't Great Circles wurkje.

De Haversine Formule

De ôfstân mei de kromming fan 'e Ierde is opnaam yn' e Haversine formule, dy't trigonometry brûkt om de kromming fan 'e ierde mooglik te meitsjen. As jo ​​de ôfstân fine tusken 2 plakken op ierde (as de kraai fljocht), is in rjochte line echt in bôge.

Dit is fan tapassing op loftflecht - hawwe jo ea sjoen nei de wirklike kaart fan flechten en hawwe jo opmurken dat se bôgje? Dat komt om't it koarter is om yn in bôge te fleanen tusken twa punten dan direkt nei de lokaasje.

PHP: Berekkenje Ofstân Tusken 2 Punten fan Breedtegraad en Lingtegraad

Hoe dan ek, hjir is de PHP-formule foar it berekkenjen fan de ôfstân tusken twa punten (tegearre mei Mile vs. Kilometer-konverzje) rûn nei twa desimale plakken.

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: Alle records binnen in berik ophelje troch berekkenjen fan ôfstân yn milen mei help fan breedte en lingtegraad

It is ek mooglik SQL te brûken om in berekkening te dwaan om alle records te finen binnen in spesifike ôfstân. Yn dit foarbyld sil ik MyTable opfreegje yn MySQL om alle records te finen dy't minder dan of gelyk binne oan fariabele $ ôfstân (yn Miles) nei myn lokaasje op $ breedtegraad en $ lingtegraad:

De fraach foar it opheljen fan alle records binnen in spesifyk ôfstân troch berekkenjen fan ôfstân yn milen tusken twa breedte- en breedtegraad binne:

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

Jo moatte dit oanpasse:

  • $ lingtegraad - dit is in PHP-fariabele wêr't ik de lingtegraad fan it punt trochjaan.
  • $ breedtegraad - dit is in PHP-fariabele wêr't ik de lingtegraad fan it punt trochjaan.
  • $ ôfstân - dit is de ôfstân dy't jo alle records wolle fine minder as gelyk oan.
  • table - dit is de tabel ... jo wolle dat ferfange troch jo tafelnamme.
  • breedte - dit is it fjild fan jo breedtegraad.
  • lingtegraad - dit is it fjild fan jo lingtegraad.

SQL: Alle records binnen in berik ophelje troch ôfstân yn kilometers te berekkenjen mei help fan breedte en lingtegraad

En hjir is de SQL-fraach dy't kilometers brûkt yn 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."

Jo moatte dit oanpasse:

  • $ lingtegraad - dit is in PHP-fariabele wêr't ik de lingtegraad fan it punt trochjaan.
  • $ breedtegraad - dit is in PHP-fariabele wêr't ik de lingtegraad fan it punt trochjaan.
  • $ ôfstân - dit is de ôfstân dy't jo alle records wolle fine minder as gelyk oan.
  • table - dit is de tabel ... jo wolle dat ferfange troch jo tafelnamme.
  • breedte - dit is it fjild fan jo breedtegraad.
  • lingtegraad - dit is it fjild fan jo lingtegraad.

Ik brûkte dizze koade yn in platfoarm foar bedriuwsmapping dat wy brûkten foar in winkel mei mear dan 1,000 lokaasjes yn Noard-Amearika en it wurke prachtich.

76 Comments

  1. 1

    Tige tank foar it dielen. Dit wie in maklike taak kopiearje en plakke en wurket geweldich. Jo hawwe my in protte tiid bewarre.
    FYI foar elkenien dy't nei C porteart:
    dûbele deg2rad (dûbele deg) {return deg * (3.14159265358979323846 / 180.0); }

  2. 2

    Hiel moai stikje pleatsing - wurke heul moai - ik hoegde allinich de namme fan 'e tafel te feroarjen mei de lat-long. It wurket frij fluch om .. Ik haw in ridlik lyts oantal lat-longs (<400), mar ik tink dat dit moai skaalje soe. Moaie side ek - ik haw it krekt tafoege oan myn del.icio.us-akkount en sil geregeld weromkomme.

  3. 4
  4. 5

    Ik socht de heule dei nei berekkeningen op ôfstân en fûn it harversine-algoritme, tank oan jo foar it jaan fan it foarbyld oer hoe't jo it yn in SQL-ferklearring kinne sette. Tank en groet, Daniel

  5. 8

    ik tink dat jo SQL in hawwende ferklearring nedich is.
    yn plak fan WHERE ôfstân <= $ ôfstân moatte jo miskien moatte
    brûk HAVING ôfstân <= $ ôfstân

    oars tank foar it besparjen fan my in soad tiid en enerzjy.

  6. 10
  7. 11
  8. 12

    Tige tank foar it dielen fan dizze koade. It besparre my in soad ûntwikkeltiid. Ek tank oan jo lêzers foar it oanwizen dat in HAVING-ferklearring nedich is foar MySQL 5.x. Tige behelpsum.

  9. 14
  10. 15
  11. 16

    Ik fûn ek dat WHERE net foar my wurke. Feroare it nei HAVING en alles wurket perfekt. Earst haw ik de opmerkingen net lêzen en it opnij skreaun mei in nestele seleksje. Beide sille gewoan goed wurkje.

  12. 17
  13. 18

    Ungelokkich behelpsum, tige tank! Ik hie wat problemen mei it nije "HAVING", ynstee fan "WHERE", mar ien kear haw ik hjir de opmerkingen lêzen (nei sawat in heal oere fan myn tosken slypje yn frustraasje = P), krige ik it moai. Tankewol ^ _ ^

  14. 19
  15. 20

    Tink derom dat in selekte ferklearring lykas dat tige berekkenjend yntensyf en dêrom stadich wêze sil. As jo ​​in protte fan dy fragen hawwe, kin it dingen fluch falle.

    In folle minder yntensive oanpak is it útfieren fan in earste (rûge) seleksje mei in SQUARE-gebiet definieare troch in berekkene ôfstân, dws "selektearje * fan tabelnamme wêr't breedte tusken lat1 en lat2 en lingtegraad tusken lon1 en lon2". lat1 = doelberjocht - latdiff, lat2 = doelberjocht + latdiff, fergelykber mei lon. latdiff ~ = ôfstân / 111 (foar km), of ôfstân / 69 foar milen, om't 1 breedtegraad ~ 111 km is (lichte fariaasje om't ierde licht ovaal is, mar genôch foar dit doel). londiff = ôfstân / (abs (cos (deg2rad (breedtegraad)) * 111)) - of 69 foar milen (jo kinne eins in wat grutter fjouwerkant nimme om ferantwurden te rekkenjen). Nim dan it resultaat dêrfan en fier it yn 'e radiale seleksje. Ferjit gewoan net te rekkenjen mei koördinaten bûten de grinzen - dat wol sizze it berik fan akseptabele lingtegraad is -180 oant +180 en it berik fan akseptabele breedtegraad is -90 oant +90 - yn it gefal dat jo latdiff of londiff bûten dit berik rint , Tink derom dat dit yn 'e measte gefallen miskien net fan tapassing is, om't it allinich berekkeningen beynfloedet oer in line troch de Stille Oseaan fan poal nei poal, hoewol it in diel fan' e chukotka en in diel fan Alaska snijt.

    Wat wy hjirmei berikke is in signifikante reduksje yn it oantal punten wêrmei jo dizze berekkening meitsje. As jo ​​in miljoen globale punten hawwe yn 'e databank rûchwei gelijkmatig ferdield en jo wolle sykje binnen 100 km, dan is jo earste (snelle) sykopdracht fan in gebiet 10000 sq km en sil wierskynlik sawat 20 resultaten opleverje (basearre op gelikense ferdieling oer in oerflak fan sawat 500M sq km), wat betsjut dat jo de komplekse ôfstânberekkening 20 kear útfiere foar dizze fraach ynstee fan in miljoen kear.

    • 21
      • 22

        Fantastysk advys! Ik wurke eins mei in ûntwikkelder dy't in funksje skreau dy't it binnenste fjouwerkant luts en dan in rekursive funksje dy't 'kwadraten' makke om 'e perimeter om de oerbleaune punten op te nimmen en út te sluten. It resultaat wie in ûnfoarstelber rap resultaat - hy koe miljoenen punten yn mikrosekonden evaluearje.

        Myn oanpak hjirboppe is definityf 'rau', mar yn steat. Nochris tank!

        • 23

          Doug,

          Ik haw besocht mysql en php te brûken om te evaluearjen oft in lat lang punt binnen in polygoon is. Witte jo as jo freon fan 'e ûntwikkelders foarbylden publisearre oer hoe't jo dizze taak kinne útfiere? Of kenne jo goede foarbylden. Alfêst tank.

  16. 24

    Hoi allegear, dit is myn test SQL-ferklearring:

    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

    en Mysql fertelt my dat ôfstân, bestiet net as kolom, ik kin oarder brûke troch, ik kin it dwaan sûnder WHERE, en it wurket, mar net mei ...

  17. 26

    Dit is geweldich, lykwols is it krekt as de fûgels fleane. It soe geweldich wêze om de google maps API hjirmei te besykjen (miskien mei diken ensfh.) Krekt om in idee te jaan mei in oare foarm fan ferfier. Ik moat noch in simulearre annealingsfunksje yn PHP meitsje dy't in effisjinte oplossing oanbiede kin foar it reizgjende ferkeaperprobleem. Mar ik tink dat ik miskien wat fan jo koade opnij brûke kin om dat te dwaan.

  18. 27
  19. 28

    Goed artikel! Ik fûn in soad artikels dy't beskreaune hoe't jo ôfstân kinne berekkenje tusken twa punten, mar ik socht echt nei it SQL-snippet.

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

    2 dagen ûndersyk om dizze pagina einlings te finen dy't myn probleem oplost. It liket derop dat ik myn WolframAlpha better útbrekke kin en myn wiskunde opromje. De feroaring fan WHERE nei HAVING hat myn skript yn wurkende oarder. DANKEWOL

  25. 37
  26. 39

    Ik winskje dat dit de earste pagina wie dy't ik hjiroer fûn. Nei it besykjen fan in protte ferskillende kommando's wie dit de iennige dy't goed wurke, en mei minimale feroaringen nedich om te passen yn myn eigen database.
    Tige dank!

  27. 40

    Ik winskje dat dit de earste pagina wie dy't ik hjiroer fûn. Nei it besykjen fan in protte ferskillende kommando's wie dit de iennige dy't goed wurke, en mei minimale feroaringen nedich om te passen yn myn eigen database.
    Tige dank!

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

    Ik wit dat dizze formule wurket, mar ik kin net sjen wêr't de striel fan 'e ierde wurdt rekken holden. Kin immen my asjebleaft ferljochtsje?

  34. 49
  35. 50
  36. 52
  37. 53
  38. 55
  39. 56
  40. 58

    tank foar it pleatsen fan dit nuttige artikel,  
    mar om wat reden soe ik it freegje wolle
    hoe kin ik de ôfstân krije tusken koördinaten yn mysql db en koördinaten ynfoege nei php troch brûker?
    om dúdliker te beskriuwen:
    1. Brûkers moatte [id] ynfoegje foar it selektearjen fan opjûne gegevens út de koördinaasjes fan db en brûker sels
    2. it php-bestân krijt de doelgegevens (koörden) mei [id] en berekkenje dan ôfstân tusken brûker en doelpunt

    of kin gewoan ôfstân krije fan 'e koade hjirûnder?

    $ qry = “SELECT *, (((acos (sin ((“. $ latitude. ”* pi () / 180)) * sin ((` Lingtegraad` * pi () / 180)) + cos ((“. $ breedtegraad. ”* pi () / 180)) * cos ((` Lingtegraad` * pi () / 180)) * cos ((((". $ lingtegraad." - `Lingtegraad`) * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) as ôfstân FAN `MyTable` WHERE ôfstân> =“. $ Ôfstân. ” >>>> kin ik de ôfstân hjirwei "úthelje"?
    nochris tank,
    Timmy S.

  41. 60

    ok, alles wat ik haw besocht, wurket net. Ik bedoel, wat ik haw wurket, mar de ôfstannen binne wei.

    Koe immen miskien sjen wat der mis is mei dizze koade?

    as (isset ($ _ POST ['yntsjinne'])) {$ z = $ _POST ['postkoade']; $ r = $ _POST ['striel']; echo "Resultaten foar". $ 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. stêd, z1.state FAN mrk m, zip z1, zip z2 WAAR m.zipcode = z1.zipcode EN z2.zipcode = $ z EN (3963 * acos (ôfkoarte (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 ") of stjerre (mysql_error ()); wylst ($ row = mysql_fetch_array ($ sql)) {$ store1 = $ row ['MktName']. "”; $ store = $ row ['LocAddSt']. ””; $ winkel. = $ rige ['LocAddCity']. ”,“. $ rige ['LocAddState']. ” “. $ Rige ['postkoade']; $ latitude1 = $ rige ['lat']; $ longitude1 = $ rige ['lon']; $ latitude2 = $ rige ['y1']; $ longitude2 = $ rige ['x1']; $ stêd = $ rige ['stêd']; $ state = $ row ['state']; $ dis = getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi'); // $ dis = ôfstân ($ lat1, $ lon1, $ lat2, $ lon2); $ ferifieare = $ rige ['ferifieare']; as ($ ferifieare == '1') {echo “”; echo "". $ winkel. ""; echo $ dis. " fier fuort"; echo ""; } oars {echo "". $ winkel. ""; echo $ dis. " fier fuort"; echo ""; }}}

    myn funksjes.php koade
    funksje getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi') {$ theta = $ longitude1 - $ longitude2; $ ôfstân = (sin (deg2rad ($ latitude1)) * sin (deg2rad ($ latitude2))) + (cos (deg2rad ($ latitude1)) * cos (deg2rad ($ latitude2)) * cos (deg2rad ($ theta)) ); $ ôfstân = acos ($ ôfstân); $ ôfstân = rad2deg ($ ôfstân); $ ôfstân = $ ôfstân * 60 * 1.1515; wikselje ($ unit) {case 'Mi': break; saak 'Km': $ ôfstân = $ ôfstân * 1.609344; } werom (rûn ($ ôfstân, 2)); }

    Dankje jo foarôfgeand

  42. 61
  43. 62

    Hoi Douglas, geweldich artikel. Ik fûn jo útlis oer de geografyske konsepten en de koade echt ynteressant. Myn iennichste suggestje soe wêze om de koade foar werjaan te pleatsen en te ynspringen (lykas Stackoverflow, bygelyks). Ik begryp dat jo romte wolle besparje, mar konvinsjonele koadeafstân / ynspringing soe it my as programmeur in stik makliker meitsje om te lêzen en te dissekearjen. Hoe dan ek, dat is in lyts ding. Trochgean mei it geweldige wurk.

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

    it liket rapper (mysql 5.9) om twa kear de formule te brûken yn 'e seleksje en wêr:
    $ formule = “((((acos (sin ((“. $ breedtegraad. ”* pi () / 180)) * sin ((` Lingtegraad` * pi () / 180)) + cos ((“. $ breedtegraad. ”* Pi () / 180)) * cos ((` Lingtegraad` * pi () / 180)) * cos (((". $ Lingtegraad." - `Lingtegraad`) * pi () / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) ”;
    $ sql = 'SELECT *,'. $ formula. ' as ôfstân FAN tafel WAAR '.. $ formule.' <= '. $ ôfstân;

  51. 71
  52. 72

    Tige tank foar skeare dit artikel. It is heul nuttich.
    PHP waard earst makke as in ienfâldich skriptplatfoarm neamd "Persoanlike thússide". Tsjintwurdich is PHP (de koarts foar Hypertext Preprocessor) in alternatyf fan de Microsoft Active Server Pages (ASP) technology.

    PHP is in iepen boarne server-side-taal dy't wurdt brûkt foar it meitsjen fan dynamyske websiden. It kin wurde ynbêde yn HTML. PHP wurdt normaal brûkt yn kombinaasje mei in MySQL-database op Linux / UNIX-webservers. It is wierskynlik de populêrste skripttaal.

  53. 73

    Ik fûn boppesteande oplossing net goed.
    Ik moat feroarje nei:

    $ qqq = “SELECT *, (((acos (sin ((“. $ latitude. ”* pi () / 180)) * sin ((` latt` * pi () / 180)) + cos ((”. $ breedtegraad. “* pi () / 180)) * cos ((` latt` * pi () / 180)) * cos ((((.. $ longitude. “-` longt`) * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515) as ôfstân FAN `register`";

  54. 75
  55. 76

    Hallo, asjebleaft sil ik jo help hjir echt nedich wêze.

    Ik haw in get-fersyk dien oan myn webserver http://localhost:8000/users/findusers/53.47792/-2.23389/20/
    53.47792 = $ breedtegraad
    -2.23389 = $ lingtegraad
    en 20 = de ôfstân dy't ik weromhelje wol

    As jo ​​jo formule brûke, hellet it lykwols alle rigen yn myn db op

    $ resultaten = DB :: selektearje (DB :: rau (“SELECT *, (((acos (sin ((“. $ latitude. ”* pi () / 180)) * sin ((lat * pi () / 180 )) + cos ((“. $ breedtegraad.” * pi () / 180)) * cos ((lat * pi () / 180)) * cos (((“. $ lingtegraad.” - lng) * pi ( ) / 180))))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) as ôfstân FAN markearders HAVING ôfstân> = “. $ Ôfstân));

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

    Ik wol gewoan rigen mei 20 kilometer weromhelje, mar it bringt alle rigen. Asjebleaft wat doch ik ferkeard

Wat tinksto?

Dizze side brûkt Akismet om spam te ferleegjen. Learje hoe't jo kommentaargegevens ferwurke wurde.