PHP en SQL: bereken of vraag groot sirkelafstand tussen breedte- en lengtepunte met die Haversine-formule

Haversine Formula - Bereken groot sirkelafstand met PHP of MySQL

Hierdie maand het ek nogal in PHP en MySQL geprogrammeer met betrekking tot GIS. Ek het eintlik 'n bietjie gesukkel om die internet te soek Geografiese berekeninge om die afstand tussen twee plekke te vind, so ek wou dit hier deel.

Vliegkaart Europa met groot sirkelafstand

Die eenvoudige manier om 'n afstand tussen twee punte te bereken, is deur die Pythagorese formule te gebruik om die skuinssy van 'n driehoek te bereken (A² + B² = C²). Dit staan ​​bekend as die Euklidiese afstand.

Dit is 'n interessante begin, maar dit geld nie vir geografie nie, aangesien die afstand tussen breedte- en lengtelyne is nie 'n gelyke afstand nie uitmekaar. Wanneer u nader aan die ewenaar kom, word breedtelyne verder uitmekaar. As u 'n soort eenvoudige driehoeksvergelyking gebruik, kan dit die afstand op die een plek akkuraat meet en verskriklik verkeerd op die ander plek as gevolg van die kromming van die Aarde.

Groot sirkelafstand

Die roetes wat lang afstande rondom die aarde afgelê word, staan ​​bekend as die Groot sirkelafstand. Dit is ... die kortste afstand tussen twee punte op 'n sfeer verskil van die punte op 'n plat kaart. Kombineer dit met die feit dat die breedte- en lengtelyne nie ewe ver is nie ... en u het 'n moeilike berekening.

Hier is 'n fantastiese video-uiteensetting van hoe Great Circles werk.

Die Haversine-formule

Die afstand met behulp van die kromming van die Aarde is opgeneem in die Haversine formule, wat trigonometrie gebruik om die kromming van die aarde moontlik te maak. As u die afstand tussen twee plekke op aarde vind (soos die kraai vlieg), is 'n reguit lyn 'n boog.

Dit is van toepassing op lugvlug - het u al ooit na die werklike kaart van vlugte gekyk en opgemerk dat dit geboë is? Dit is omdat dit korter is om tussen twee punte in 'n boog te vlieg as direk na die plek.

PHP: Bereken afstand tussen 2 punte van breedte en lengte

Hoe dit ook al sy, hier is die PHP-formule vir die berekening van die afstand tussen twee punte (saam met Mile versus Kilometer-omskakeling) afgerond tot twee desimale plekke.

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 rekords binne 'n reeks haal deur afstand in myle te bereken met behulp van breedte- en lengtegraad

Dit is ook moontlik om SQL te gebruik om 'n berekening te doen om alle rekords binne 'n spesifieke afstand te vind. In hierdie voorbeeld gaan ek MyTable in MySQL navraag doen om al die rekords te vind wat kleiner as of gelyk is aan die veranderlike $ afstand (in Miles) na my plek op $ breedtegraad en $ lengte:

Die navraag om al die rekords in 'n spesifieke op te spoor afstand deur die afstand in myl te bereken tussen twee breedte- en lengtepunte is:

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

U moet dit aanpas:

  • $ lengte - dit is 'n PHP-veranderlike waar ek die lengte van die punt deurgee.
  • $ breedtegraad - dit is 'n PHP-veranderlike waar ek die lengte van die punt deurgee.
  • $ afstand - dit is die afstand waarop u al die rekords minder of gelyk wil vind.
  • tafel - dit is die tabel ... u wil dit vervang deur u tafelnaam.
  • breedtegraad - dit is die veld van u breedtegraad.
  • lengtegraad - dit is die veld van u lengtegraad.

SQL: haal alle rekords binne 'n reeks deur afstand in kilometers te bereken met behulp van breedte- en lengtegraad

En hier is die SQL-vraag wat kilometers in MySQL gebruik:

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

U moet dit aanpas:

  • $ lengte - dit is 'n PHP-veranderlike waar ek die lengte van die punt deurgee.
  • $ breedtegraad - dit is 'n PHP-veranderlike waar ek die lengte van die punt deurgee.
  • $ afstand - dit is die afstand waarop u al die rekords minder of gelyk wil vind.
  • tafel - dit is die tabel ... u wil dit vervang deur u tafelnaam.
  • breedtegraad - dit is die veld van u breedtegraad.
  • lengtegraad - dit is die veld van u lengtegraad.

Ek het hierdie kode gebruik in 'n kaarteplatform vir ondernemings wat ons gebruik het vir 'n kleinhandelwinkel met meer as 1,000 XNUMX plekke in Noord-Amerika, en dit het pragtig gewerk.

77 Comments

  1. 1

    Baie dankie vir die deel. Dit was 'n maklike kopieer- en plakwerk en werk uitstekend. U het my baie tyd bespaar.
    FYI vir almal wat na C stuur:
    dubbel deg2rad (dubbel deg) {retour deg * (3.14159265358979323846 / 180.0); }

  2. 2

    Baie mooi boodskap - baie goed gewerk - ek moes net die naam van die tafel verander wat die lengte bevat. Dit werk redelik vinnig tot .. Ek het 'n redelike klein aantal lang lengtes (<400), maar ek dink dit sal mooi afskaal. Mooi webwerf ook - ek het dit pas by my del.icio.us-rekening gevoeg en sal gereeld terugkom.

  3. 4
  4. 5
  5. 8

    ek dink dat u SQL 'n verklaring nodig het.
    in plaas van WAAR afstand <= $ afstand wat u nodig het
    gebruik HAVING afstand <= $ afstand

    anders dankie dat jy 'n klomp tyd en energie bespaar.

  6. 10
  7. 11
  8. 12

    Baie dankie vir die deel van hierdie kode. Dit het my baie ontwikkelingstyd bespaar. Dank u ook aan u lesers wat daarop gewys het dat 'n HAVING-verklaring nodig is vir MySQL 5.x. Baie behulpsaam.

  9. 14
  10. 15

    Hallo,

    Nog 'n vraag. Is daar 'n formule vir NMEA-snare soos die onderstaande?

    1342.7500, N, 10052.2287, E

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

    Dankie,
    Harry

  11. 16

    Ek het ook gevind dat WAAR nie vir my werk nie. Het dit verander na HAVING en alles werk perfek. Aanvanklik het ek nie die opmerkings gelees nie en dit herskryf met behulp van 'n geneste keuse. Albei sal net goed werk.

  12. 17
  13. 18

    Ongelooflik behulpsaam, baie dankie! Ek het probleme ondervind met die nuwe "HAVING", eerder as "WAAR", maar sodra ek die kommentaar hier gelees het (na ongeveer 'n halfuur van my tande in frustrasie = P), het ek dit goed laat werk. Dankie ^ _ ^

  14. 19
  15. 20

    Hou in gedagte dat so 'n uitspraak baie berekenend intens sal wees en dus stadig sal wees. As u baie van hierdie navrae het, kan dit dinge vinnig vasval.

    'N Veel minder intense benadering is om 'n eerste (ruwe) seleksie uit te voer met behulp van 'n SQUARE-area, gedefinieër deur 'n berekende afstand, dws' kies * van die tabelnaam waar breedtegraad tussen lat1 en lat2 en lengte tussen lon1 en lon2 '. lat1 = doelgebied - latdiff, lat2 = doelgebied + latdiff, soortgelyk aan lon. latdiff ~ = afstand / 111 (vir km), of afstand / 69 vir myl aangesien 1 breedtegraad ~ 111 km is (effense variasie aangesien die aarde effens ovaal is, maar voldoende is vir hierdie doel). londiff = afstand / (abs (cos (deg2rad (breedtegraad)) * 111)) - of 69 vir myl (u kan eintlik 'n effens groter vierkant neem om rekening te hou met variasies). Neem dan die resultaat daarvan en voer dit in die radiale seleksie. Moet net nie vergeet om koördinate buite die perke in ag te neem nie - dit wil sê die omvang van die aanvaarbare lengte is -180 tot +180 en die omvang van die aanvaarbare breedte is -90 tot +90 - as u latiff of londiff buite hierdie reeks loop . Let daarop dat dit meestal nie van toepassing is nie, aangesien dit slegs berekeninge oor 'n lyn deur die Stille Oseaan van pool tot pool beïnvloed, alhoewel dit 'n gedeelte van chukotka en 'n gedeelte van Alaska sny.

    Wat ons hiermee bereik, is 'n beduidende afname in die aantal punte waarteen u hierdie berekening maak. As u 'n miljoen globale punte in die databasis het wat ongeveer eweredig versprei is en u binne 100 km wil soek, dan is u eerste (vinnige) soektog 'n oppervlakte van 10000 vierkante km en sal dit waarskynlik ongeveer 20 resultate lewer (gebaseer op eweredige oppervlakte van ongeveer 500 M vierkante km), wat beteken dat u die komplekse afstandberekening 20 keer vir hierdie navraag in plaas van 'n miljoen keer uitvoer.

    • 21
      • 22

        Fantastiese raad! Ek het eintlik saam met 'n ontwikkelaar gewerk wat 'n funksie geskryf het wat die binnekant van die vierkant getrek het en dan 'n rekursiewe funksie wat 'vierkante' rondom die omtrek gemaak het om die oorblywende punte in te sluit en uit te sluit. Die resultaat was 'n ongelooflike vinnige resultaat - hy kon miljoene punte in mikrosekondes evalueer.

        My benadering hierbo is beslis 'kru', maar in staat. Weereens dankie!

        • 23

          Doug,

          Ek het mysql en php probeer gebruik om te evalueer of 'n lang punt binne 'n veelhoek is. Weet u of u ontwikkelaarvriend voorbeelde gepubliseer het oor hoe u hierdie taak kan uitvoer? Of ken u goeie voorbeelde. Dankie by voorbaat.

  16. 24

    Hallo almal dit is my toets SQL-stelling:

    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 vertel my dat die afstand nie as 'n kolom bestaan ​​nie, ek kan orde deur gebruik, ek kan dit doen sonder WAAR, en dit werk, maar nie daarmee nie ...

  17. 26

    Dit is wonderlik, maar dit is net soos die voëls vlieg. Dit sal wonderlik wees om die Google Maps API op die een of ander manier hierby in te werk (miskien met behulp van paaie, ens.) Net om 'n idee te gee met 'n ander vorm van vervoer. Ek moet nog 'n gesimuleerde uitgloeiingsfunksie in PHP maak wat 'n doeltreffende oplossing vir die reisverkoperprobleem kan bied. Maar ek dink dat ek dalk van u kode kan hergebruik om dit te doen.

  18. 27
  19. 28

    Goeie artikel! Ek het baie artikels gevind wat beskryf hoe ek afstand tussen twee punte kan bereken, maar ek was regtig op soek na die SQL-brokkie.

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

    2 dae se navorsing om uiteindelik hierdie bladsy te vind wat my probleem oplos. Dit wil voorkom of ek my WolframAlpha beter kan uitbreek en my wiskunde kan opruim. Die verandering van WHERE na HAVING het my teks in werkende toestand. DANKIE

  25. 37
  26. 39

    Ek wens dit was die eerste bladsy wat ek hieroor gevind het. Nadat ek baie verskillende opdragte probeer het, was dit die enigste wat behoorlik werk, en met die minimum veranderinge wat nodig was om in my eie databasis te pas.
    Thanks a lot!

  27. 40

    Ek wens dit was die eerste bladsy wat ek hieroor gevind het. Nadat ek baie verskillende opdragte probeer het, was dit die enigste wat behoorlik werk, en met die minimum veranderinge wat nodig was om in my eie databasis te pas.
    Thanks a lot!

  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

    dankie vir die plasing van hierdie nuttige artikel,  
    maar om die een of ander rede wil ek dit vra
    hoe kry ek die afstand tussen koorde in mysql db en koördas wat deur php by php ingevoeg is?
    om dit duideliker te beskryf:
    1. Gebruiker moet [id] invoeg vir die keuse van gespesifiseerde data uit db en die gebruiker self se koördinate
    2. kry die php-lêer die teikengegewens (koorde) met behulp van [id] en bereken dan die afstand tussen gebruiker en teikenpunt

    of kan u bloot afstand kry van die onderstaande kode?

    $ qry = “SELECT *, (((acos (sin ((“. $ latitude. ”* pi () / 180)) * sin ((` Latitude` * pi () / 180)) + cos ((“. $ breedtegraad. ”* pi () / 180)) * cos ((` Breedtegraad` * pi () / 180)) * cos ((((. $ longitude. - Longitude) pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) as afstand VANAF `MyTable` WAAR afstand> =“. $ Afstand. ” >>>> Kan ek die afstand hiervandaan "uithaal"?
    weereens dankie,
    Timmy S.

  41. 60

    ok, alles wat ek probeer het, werk nie. Ek bedoel, wat ek het, werk, maar die afstande is ver.

    Kan iemand sien wat verkeerd is met hierdie kode?

    as (isset ($ _ POST ['ingedien'])) {$ z = $ _POST ['poskode']; $ r = $ _POST ['radius']; eggo “Resultate vir“. $ 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. stad, z1.staat VANAF mrk m, zip z1, zip z2 WAAR m.zipcode = z1.zipcode EN z2.zipcode = $ z AND (3963 * acos (afgesny (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 sterf (mysql_error ()); terwyl ($ row = mysql_fetch_array ($ sql)) {$ store1 = $ row ['MktName']. "”; $ store = $ row ['LocAddSt']. ””; $ store. = $ row ['LocAddCity']. ”,“. $ row ['LocAddState']. ” “. $ Row ['zipcode']; $ latitude1 = $ ry ['lat']; $ longitude1 = $ ry ['lon']; $ latitude2 = $ ry ['y1']; $ longitude2 = $ ry ['x1']; $ stad = $ ry ['stad']; $ state = $ row ['state']; $ dis = getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi'); // $ dis = afstand ($ lat1, $ lon1, $ lat2, $ lon2); $ geverifieerd = $ ry ['geverifieerd']; as ($ geverifieer == '1') {echo “”; eggo "". $ store. ""; eggo $ dis. " myle weg"; eggo “”; } anders {eggo “”. $ store. ””; eggo $ dis. " myle weg"; eggo “”; }}}

    my funksies.php-kode
    funksie getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi') {$ theta = $ longitude1 - $ longitude2; $ afstand = (sin (deg2rad ($ latitude1)) * sin (deg2rad ($ latitude2))) + (cos (deg2rad ($ latitude1)) * cos (deg2rad ($ latitude2)) * cos (deg2rad ($ theta)) ); $ afstand = acos ($ afstand); $ afstand = rad2deg ($ afstand); $ afstand = $ afstand * 60 * 1.1515; skakel ($ eenheid) {geval 'Mi': breek; geval 'Km': $ afstand = $ afstand * 1.609344; } terugkeer (rond ($ afstand, 2)); }

    Dankie by voorbaat

  42. 61
  43. 62

    Haai Douglas, wonderlike artikel. Ek het u verduideliking van die geografiese begrippe en die kode regtig interessant gevind. My enigste voorstel is om die kode vir vertoon (soos byvoorbeeld Stackoverflow) te spasieer en in te trek. Ek verstaan ​​dat u ruimte wil bespaar, maar konvensionele kodespasiëring / inkeping sou dit vir my as programmeerder baie makliker maak om te lees en te dissekteer. Dit is in elk geval 'n klein dingetjie. Hou aan met die wonderlike werk.

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

    Dit lyk vinniger (mysql 5.9) om twee keer die formule in die select te gebruik en waar:
    $ formule = “((((acos (sin ((“. $ breedtegraad. ”* pi () / 180)) * sin ((` Breedtegraad` * pi () / 180)) + cos ((“. $ breedtegraad. ”* Pi () / 180)) * cos ((` Breedtegraad` * pi () / 180)) * cos (((“. $ Longitude.” - `Longitude`) * pi () / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) ”;
    $ sql = 'SELECT *,'. $ formule. ' as afstand VANAF tafel WAAR '.. $ formule.' <= '. $ afstand;

  51. 71
  52. 72

    Baie dankie vir hierdie artikel. Dit is baie nuttig.
    PHP is eers geskep as 'n eenvoudige skrip-platform genaamd 'Persoonlike tuisblad'. Tans is PHP (die afkorting vir Hypertext Preprocessor) 'n alternatief vir Microsoft se Active Server Pages (ASP) -tegnologie.

    PHP is 'n oop bron-bediener-taal wat gebruik word vir die skep van dinamiese webblaaie. Dit kan in HTML ingebed word. PHP word gewoonlik saam met 'n MySQL-databasis op Linux / UNIX-webbedieners gebruik. Dit is waarskynlik die gewildste skriptaal.

  53. 73

    Ek het gevind dat die oplossing hierbo nie behoorlik werk nie.
    Ek moet verander na:

    $ 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 afstand VAN 'register' ';

  54. 75
  55. 76

    Hallo, ek sal regtig u hulp hieroor nodig hê.

    Ek het 'n versoek vir my webbediener gerig http://localhost:8000/users/findusers/53.47792/-2.23389/20/
    53.47792 = $ breedtegraad
    -2.23389 = $ lengte
    en 20 = die afstand wat ek wil haal

    Alhoewel u u formule gebruik, haal dit alle rye in my db op

    $ resultate = DB :: kies (DB :: rou (“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) as afstand VANAF merkers HAVING distance> = “. $ Distance));

    [{"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, ”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, 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}]

    Ek wil net rye met 20 myl haal, maar dit bring alle rye. Asseblief, wat doen ek verkeerd?

  56. 77

    Ek is op soek na 'n soortgelyke navraag, maar het 'n bietjie versterk - dit is kortliks om alle koördinate binne 2 myl van elke koördinaat te groepeer en dan hoeveel koördinate in elke groep te tel en slegs een groep met die meeste koördinate uit te voer - selfs al jy het meer as een groep onder die groepe met die meeste koördinate - voer eenvoudig die ewekansige groep uit die groepe met dieselfde grootste getal uit -

Wat dink jy?

Hierdie webwerf gebruik Akismet om spam te verminder. Leer hoe jou opmerking verwerk is.