PHP és SQL: Számítsa ki vagy kérdezze meg a nagy körtávolságot a szélességi és hosszúsági pontok között a Haversine képlettel

Haversine Formula - Számítsa ki a nagy körtávolságot PHP vagy MySQL használatával

Ebben a hónapban elég sokat programoztam PHP-ben és MySQL-ben a térinformatika tekintetében. A háló körül szaglászva valóban nehezen találtam meg néhányat Földrajzi számítások hogy megtalálja a távolságot két helyszín között, ezért itt szerettem volna megosztani őket.

Repülőtér Európa nagy körtávolsággal

A két pont közötti távolság kiszámításának egyszerű módja a Pitagorasz-képlet segítségével egy háromszög hipotenuszának kiszámítása (A² + B² = C²). Ez az úgynevezett Euklideszi távolság.

Ez egy érdekes kezdet, de nem vonatkozik a földrajzra, mivel a szélességi és hosszúsági vonalak közötti távolság igen nem egyenlő távolság egymástól. Ahogy közelebb kerülsz az Egyenlítőhöz, a szélességi vonalak egyre távolabb kerülnek egymástól. Ha valamilyen egyszerű háromszögelési egyenletet használ, akkor a Föld görbülete miatt az egyik helyen pontosan mérheti a távolságot, a másikban pedig rettenetesen rosszul.

Nagy körtávolság

A Föld körül nagy távolságokat megtett útvonalak ún Nagy körtávolság. Vagyis ... a gömb két pontja közötti legrövidebb távolság eltér a lapos térkép pontjaitól. Kombinálja ezt azzal a ténnyel, hogy a szélességi és hosszúsági vonalak nem egyenlő távolságra vannak ... és nehéz számításokat kap.

Itt van egy fantasztikus videó magyarázat a Nagy Körök működéséről.

A Haversine Formula

A Föld görbületét használó távolság beépül a Haversine formula, amely trigonometria segítségével teszi lehetővé a föld görbületét. Ha megtalálja a távolságot a föld két helye között (légvonalban), akkor az egyenes valóban ív.

Ez a légi repülésnél alkalmazható - megnézte már a tényleges repülési térképet, és észrevette, hogy azok íveltek? Ennek oka, hogy rövidebb egy boltívben repülni két pont között, mint közvetlenül a helyszínre.

PHP: Számítsa ki a távolságot 2 szélességi és hosszúsági pont között

Mindenesetre itt van a PHP képlet két pont távolságának kiszámításához (a Mile vs. Kilometer konverzióval együtt) két tizedesjegyre kerekítve.

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: Az összes rekord lekérése egy tartományon belül úgy, hogy kiszámítja a mérföld távolságát a szélesség és hosszúság segítségével

Az SQL segítségével számítást is végezhet, hogy az összes rekordot megtalálja egy adott távolságon belül. Ebben a példában megkérdezem a MyTable szolgáltatást a MySQL-ben, hogy megtaláljam az összes olyan rekordot, amely kisebb vagy egyenlő, mint $ változó $ távolság (mérföldben) a helyemig $ latitude és $ longitude:

A lekérdezés egy adott rekord összes rekordjának lekérésére távolság a két szélességi és hosszúsági pont mérföldes távolságának kiszámításával:

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

Ezt testre kell szabnia:

  • $ hosszúság - ez egy PHP változó, ahol a pont hosszúságát adom át.
  • $ szélesség - ez egy PHP változó, ahol a pont hosszúságát adom át.
  • $ távolság - ez az a távolság, amellyel az összes rekordot kisebbnek vagy egyenlőnek szeretné megtalálni.
  • táblázat - ez a táblázat ... ezt le kell cserélnie a táblázat nevére.
  • szélesség - ez a szélességi köröd.
  • hosszúság - ez a hosszúságod területe.

SQL: Az összes rekord lekérése egy tartományon belül úgy, hogy kiszámítja a távolságot kilométerben a szélesség és hosszúság felhasználásával

És íme az SQL lekérdezés, amely kilométereket használ a MySQL-ben:

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

Ezt testre kell szabnia:

  • $ hosszúság - ez egy PHP változó, ahol a pont hosszúságát adom át.
  • $ szélesség - ez egy PHP változó, ahol a pont hosszúságát adom át.
  • $ távolság - ez az a távolság, amellyel az összes rekordot kisebbnek vagy egyenlőnek szeretné megtalálni.
  • táblázat - ez a táblázat ... ezt le kell cserélnie a táblázat nevére.
  • szélesség - ez a szélességi köröd.
  • hosszúság - ez a hosszúságod területe.

Ezt a kódot egy vállalati térképészeti platformon használtam fel, amelyet Észak-Amerikában több mint 1,000 telephellyel rendelkező kiskereskedelmi üzlet számára használtunk, és gyönyörűen működött.

76 Comments

  1. 1

    Nagyon köszönöm a megosztást. Ez könnyű másolási és beillesztési feladat volt, és remekül működik. Rengeteg időt spóroltál meg.
    FYI bárkinek, aki C-re portál:
    dupla deg2rad (dupla deg) {visszatér deg * (3.14159265358979323846 / 180.0); }

  2. 2

    Nagyon szép darab postázás - nagyon szépen működött - csak a lat nevét tartó asztal nevét kellett megváltoztatnom. Elég gyorsan működik, hogy .. Van egy meglehetősen kevés lat hosszúság (<400), de azt hiszem, ez szépen skálázódna. Szép oldal is - most vettem fel a del.icio.us fiókomba, és rendszeresen ellenőrizni fogom.

  3. 4
  4. 5
  5. 8

    szerintem az SQL-nek szüksége van egy rendelkező utasításra.
    a WHERE távolság <= $ távolság helyett szükséges lehet
    használja a HAVING distance <= $ távolságot

    különben köszönöm, hogy megtakarított egy csomó időt és energiát.

  6. 10
  7. 11
  8. 12

    Nagyon köszönöm, hogy megosztotta ezt a kódot. Nagyon sok fejlesztési időt spóroltam meg. Ezenkívül köszönjük olvasóinak, hogy felhívták a figyelmet arra, hogy HAVING utasítás szükséges a MySQL 5.x-hez. Nagyon hasznos.

  9. 14
  10. 15

    Helló,

    Másik kérdés. Van-e olyan formula az NMEA-húrokhoz, mint az alábbi?

    1342.7500, N, 10052.2287, E

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

    Köszönöm,
    Harry

  11. 16

    Azt is megállapítottam, hogy a WHERE nem működött nálam. Megváltoztatta HAVING-re, és minden tökéletesen működik. Eleinte nem olvastam a megjegyzéseket, és átírtam egy beágyazott kiválasztó segítségével. Mindkettő remekül fog működni.

  12. 17
  13. 18

    Hihetetlenül hasznos, köszönöm szépen! Volt néhány problémám az új „HAVING” -nel, nem pedig a „WHERE” -vel, de miután egyszer elolvastam az itt található megjegyzéseket (kb. Félórás csalódott fogcsikorgatás után = P), szépen sikerült. Köszönöm ^ _ ^

  14. 19
  15. 20

    Ne feledje, hogy egy ilyen kiválasztott állítás számítási szempontból nagyon intenzív és ezért lassú lesz. Ha sok ilyen kérdése van, akkor az elég gyorsan ellehetetlenítheti a dolgokat.

    Sokkal kevésbé intenzív megközelítés az első (nyers) kiválasztás futtatása a Négyzet terület segítségével, amelyet egy számított távolság határoz meg, azaz „select * táblanévből, ahol a lat1 és lat2 közötti szélesség és lon1 és lon2 közötti hosszúság”. lat1 = targetlatitude - latdiff, lat2 = targetlatitude + latdiff, hasonló a lon-hoz. latdiff ~ = távolság / 111 (km-re), vagy távolság / 69 mérföldre, mivel az 1 szélességi fok ~ 111 km (enyhe eltérés, mivel a föld kissé ovális, de elegendő erre a célra). londiff = távolság / (abs (cos (deg2rad (szélesség)) * 111)) - vagy 69 mérföldre (a változások figyelembevétele érdekében valójában valamivel nagyobb négyzetet vehet fel). Ezután vegye ennek eredményét és töltse be a radiális kiválasztásba. Ne felejtse el figyelembe venni a határon kívüli koordinátákat is - azaz az elfogadható hosszúság tartománya -180 és +180 között, az elfogadható szélesség tartománya pedig -90 és +90 között van, ha a latdiff vagy a londiff ezen a tartományon kívül fut . Vegye figyelembe, hogy a legtöbb esetben ez nem alkalmazható, mivel csak a Csendes-óceánon át a pólustól a pólusig terjedő egyenesen végzett számításokat érinti, bár a chukotka és az alaska egy részét keresztezi.

    Amit ezzel elérünk, az a pontok számának jelentős csökkenése, amelyekkel szemben ezt a számítást végzi. Ha az adatbázisban egymillió globális pont van, nagyjából egyenletesen elosztva, és 100 km-en belül szeretne keresni, akkor az első (gyors) keresése 10000 négyzetkilométeres területre esik, és valószínűleg körülbelül 20 eredményt fog eredményezni (az egyenletes eloszlás alapján kb. 500 millió négyzetkilométernyi terület), ami azt jelenti, hogy a komplex távolság kiszámítását milliószor 20-szor futtatja le erre a lekérdezésre.

    • 21

      Kisebb hiba a példában… ez 50 km-es (nem 100) km-es távolságon belül lenne, mivel a… négyzetünk sugarát nézzük.

      • 22

        Fantasztikus tanácsok! Valójában egy fejlesztővel dolgoztam, aki írt egy olyan függvényt, amely meghúzta a belső négyzetet, majd egy rekurzív függvényt, amely „négyzeteket” készített a kerület köré a többi pont felvételére és kizárására. Az eredmény hihetetlenül gyors eredmény volt - több millió pontot tudott mikroszekundumban értékelni.

        A fenti megközelítésem határozottan „nyers”, de képes. Köszönöm mégegyszer!

        • 23

          Doug,

          Megpróbáltam használni a mysql-t és a php-t annak értékelésére, hogy egy lat hosszú pont egy poligonon belül van-e. Tudja, hogy fejlesztő barátja publikált-e példákat a feladat végrehajtására? Vagy tudsz valami jó példát. Előre is köszönöm.

  16. 24

    Sziasztok mindenkinek, ez az én teszt SQL utasításom:

    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

    és a Mysql azt mondja nekem, hogy a távolság nem létezik oszlopként, használhatom a sorrendet, meg tudom csinálni WHERE nélkül, és működik, de nem vele ...

    • 25
  17. 26

    Ez nagyszerű, de éppen akkor repül, amikor a madarak repülnek. Nagyon jó lenne megpróbálni valahogy beépíteni a Google Maps API-t (esetleg utakat használva stb.), Hogy ötletet adjon egy másik közlekedési forma használatával. Még mindig nem készítettem egy szimulált izzítási funkciót a PHP-ben, amely képes lenne hatékony megoldást kínálni az utazó eladó problémájára. De úgy gondolom, hogy képes leszek felhasználni néhány kódját erre.

  18. 27
  19. 28

    Jó cikk! Sok cikket találtam, amelyek leírják a két pont közötti távolság kiszámítását, de nagyon kerestem az SQL-kódrészletet.

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

    2 napos kutatás, hogy végre megtaláljam ezt az oldalt, amely megoldja a problémámat. Úgy tűnik, jobb, ha kibuktatom a WolframAlphámat, és felpörgetem a matematikámat. A WHERE-ról HAVING-re történő váltáskor a szkriptem működőképes. KÖSZÖNÖM

  25. 37
    • 38

      Köszönöm Georgi. Folyton azt kaptam, hogy a "távolság" oszlop nem található. Miután megváltoztattam a WHERE-t a HAVING-re, varázslatként működött!

  26. 39

    Bárcsak ez lenne az első oldal, amit találtam ezen. Számos különféle parancs kipróbálása után ez volt az egyetlen, amely megfelelően működött, és minimális változtatásokkal kellett elvégezni a saját adatbázisomat.
    Nagyon köszönöm!

  27. 40

    Bárcsak ez lenne az első oldal, amit találtam ezen. Számos különféle parancs kipróbálása után ez volt az egyetlen, amely megfelelően működött, és minimális változtatásokkal kellett elvégezni a saját adatbázisomat.
    Nagyon köszönöm!

  28. 41
  29. 42
  30. 43
  31. 45
  32. 46
  33. 47
  34. 49
  35. 50
  36. 52

    Köszönöm Douglas, az SQL lekérdezés pontosan az, amire szükségem volt, és úgy gondoltam, hogy magamnak kell megírnom. Megmentettél a szélességi és hosszúsági órák óráitól!

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

    köszönöm, hogy közzétetted ezt a hasznos cikket,  
    de valamiért szeretném megkérdezni
    hogyan lehet elérni a távolságot a mysql db-ben és a koordinátorok között a felhasználó által a php-be?
    pontosabban írja le:
    1. a felhasználónak be kell írnia az [id] elemet a megadott adatok kiválasztásához a db-ból és a felhasználó saját koordinátáiból
    2. a php fájl megkapja a céladatokat (koordinátorokat) az [id] használatával, majd kiszámítja a felhasználó és a célpont közötti távolságot

    vagy egyszerűen csak távolságot tud szerezni az alábbi kódtól?

    $ qry = “SELECT *, (((acos (sin ((“ “. $ szélesség.” * pi () / 180)) * sin ((„Szélesség” * pi () / 180)) + cos ((„. $ szélesség. ”* pi () / 180)) * cos ((„ Szélesség ”* pi () / 180)) * cos (((„. $ hosszúság. ”-„ Hosszúság ”) * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) távolságként a „MyTable” WHERE távolságtól = = „. $ Távolság.” >>>> ki tudom-e venni innen a távolságot?
    köszönöm mégegyszer,
    Timmy S

  41. 60

    ok, minden, amit kipróbáltam, nem működik. Mármint ami működik, az működik, de a távolságok nagyon távol vannak.

    Láthatja bárki, mi a baj ezzel a kóddal?

    if (isset ($ _ POST ['beküldött'])) {$ z = $ _POST ['irányítószám']; $ r = $ _POST ['sugár']; echo „Eredmények a következőre:. $ 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. város, z1.állapot mrk m, zip z1, zip z2 HOL HOL m.zipcode = z1.zipcode ÉS z2.zipcode = $ z AND (3963 * acos (csonka (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 ") vagy meghal (mysql_error ()); while ($ row = mysql_fetch_array ($ sql)) {$ store1 = $ row ['MktName']. ""; $ store = $ row ['LocAddSt']. ””; $ store. = $ row ['LocAddCity']. ",„. $ row ['LocAddState']. " “. $ Row ['irányítószám']; $ latitude1 = $ row ['lat']; $ longitude1 = $ row ['lon']; $ latitude2 = $ sor ['y1']; $ longitude2 = $ sor ['x1']; $ város = $ sor ['város']; $ állapot = $ sor ['állam']; $ dis = getnew ($ szélesség1, $ hosszúság1, $ szélesség2, $ hosszúság2, $ mértékegység = 'Mi'); // $ dis = távolság ($ lat1, $ lon1, $ lat2, $ lon2); $ verified = $ row ['verified']; if ($ verified == '1') {visszhang ""; visszhang „”. $ áruház. ””; echo $ dis. " mérföldekkel odébb"; visszhang „”; } else {echo “”. $ áruház. ””; echo $ dis. " mérföldekkel odébb"; visszhang „”; }}}

    függvényeim.php kód
    függvény getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi') {$ theta = $ longitude1 - $ longitude2; $ távolság = (sin (deg2rad ($ szélesség1)) * sin (deg2rad ($ szélesség2))) + (cos (deg2rad ($ szélesség1)) * cos (deg2rad ($ szélesség2)) * cos (deg2rad ($ theta)) ); $ távolság = acos ($ távolság); $ távolság = rad2deg ($ távolság); $ távolság = $ távolság * 60 * 1.1515; kapcsoló ($ egység) {eset 'Mi': törés; 'Km' eset: $ távolság = $ távolság * 1.609344; } vissza (kör ($ távolság, 2)); }

    Előre is köszönöm

  42. 61
  43. 62

    Hé Douglas, remek cikk. Nagyon érdekesnek találtam a földrajzi fogalmak és a kód magyarázatát. Az egyetlen javaslatom az lenne, hogy a megjelenítéshez szükséges kódot be kell hagyni és behúzni (például a Stackoverflow-ba). Megértem, hogy meg akarja kímélni a helyet, de a hagyományos kódközök / behúzások sokkal könnyebbé tennék számomra, mint programozónak az olvasást és a boncolgatást. Mindenesetre ez egy apróság. Folytasd a nagyszerű munkát.

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

    gyorsabbnak tűnik (mysql 5.9) a képlet kétszeres használata a select-ben és ahol:
    $ formula = “(((acos (sin ((“. $ szélesség. ”* pi () / 180)) * sin ((„ Szélesség ”* pi () / 180)) + cos ((„. $ szélesség. ”* Pi () / 180)) * cos ((„ Szélesség ”* pi () / 180)) * cos (((„. $ Hosszúság. ”-„ Hosszúság ”) * pi () / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) ”;
    $ sql = 'SELECT *,'. $ formula. ' mint távolság A HOLT táblától ".. $ formula. ' <= '. $ távolság;

  51. 71
  52. 72

    Nagyon köszönöm, hogy megnyírta ezt a cikket. Nagyon hasznos.
    A PHP-t először egyszerű szkriptplatformként hozták létre, a „Personal Home Page” néven. Manapság a PHP (a Hypertext Preprocessor rövidítése) a Microsoft Active Server Pages (ASP) technológiájának alternatívája.

    A PHP egy nyílt forráskódú szerveroldali nyelv, amelyet dinamikus weboldalak létrehozására használnak. Beágyazható HTML-be. A PHP-t általában MySQL adatbázissal együtt használják Linux / UNIX webszervereken. Valószínűleg ez a legnépszerűbb szkriptnyelv.

  53. 73

    Megállapítottam, hogy a fenti megoldás nem működik megfelelően.
    Át kell váltanom:

    $ qqq = “SELECT *, (((acos (sin ((“. “$ szélesség.” * pi () / 180)) * sin ((`latt` * pi () / 180)) + cos ((". $ szélesség. “* pi () / 180)) * cos ((" latt "* pi () / 180)) * cos (((" ". $ hosszúság." - "longt") * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515) távolság a "regisztrációtól" ";

  54. 75

    köszönöm, uram tökéletesen csavarodik .. de van egy kérdésem, ha tizedespont nélkül akarok kimenetet adni, akkor mit tehetek ..?

    Előre is köszönöm.

  55. 76

    Helló, kérem, nagyon szükségem lesz a segítségedre ebben.

    Kérést küldtem a webszerveremre http://localhost:8000/users/findusers/53.47792/-2.23389/20/
    53.47792 = $ szélesség
    -2.23389 = $ hosszúság
    és 20 = az a távolság, amelyet be akarok szerezni

    Azonban az ön képletét használva lekéri az összes sort a db-mben

    $ results = DB :: select (DB :: raw (“SELECT *, ((acos (sin ((“ “. $ szélesség.” * pi () / 180)) * sin ((lat * pi () / 180 )) + cos ((“. $ szélesség.” * pi () / 180)) * cos ((lat * pi () / 180)) * cos (((“. $ hosszúság.” - lng) * pi ( ) / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) távolságként olyan markerektől, amelyeknek távolsága = = “. $ Távolság));

    [{"Id": 1, "név": "Frankie Johnnie és Luigo Too", "cím": "939 W El Camino Real, Mountain View, Kalifornia", "lat": 37.386337280273, "lng": - 122.08582305908, „Távolság”: 16079.294719663}, {„id”: 2, „név”: „Amici keleti parti pizzériája”, „cím”: „790 Castro St, Mountain View, Kalifornia”, „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 View, Kalifornia ”,” lat ”: 37.394012451172,” lng ”: - 122.09552764893,” távolság ”: 16078.563225154}, {“ id ”: 6,“ name ”:“ Oregano's Wood-Fired Pizza ”,“ address ”:” 4546 El Camino Real, Los Altos, Kalifornia ”,” lat ”: 37.401725769043,” lng ”: - 122.11464691162,“ távolság ”: 16077.937560795}, {“ id ”: 7,” name ”:” The bar and grills ”,” address ”:” 24 Whiteley Street, Manchester ”,“ lat ”: 53.485118865967,” lng ”: - 2.1828699111938,“ distance ”: 8038.7620112314}]

    Csak 20 mérföldes sorokat szeretnék lekérni, de az összes sort meghozza. Kérem, mit csinálok rosszul

Mit gondolsz?

Ez az oldal Akismet-et használ a levélszemét csökkentése érdekében. Ismerje meg, hogyan dolgozik a megjegyzésed.