PHP dhe SQL: Llogaritni ose Kërkoni distancën midis pikave të gjatësisë dhe gjatësisë

mat distancën
Koha e leximit: 3 minuta

Këtë muaj kam programuar mjaft në PHP dhe MySQL në lidhje me GIS. Duke përgjuar rrjetën, në të vërtetë e kisha të vështirë të gjeja disa nga llogaritjet gjeografike për të gjetur distancën midis dy vendndodhjeve, kështu që doja t'i ndaja këtu.

Mënyra e thjeshtë për të llogaritur një distancë midis dy pikave është përdorimi i formulës Pitagoriane për të llogaritur hipotenuzën e një trekëndëshi (A² + B² = C²). Kjo është e njohur si Distanca euklidiane.

Ky është një fillim interesant, por nuk zbatohet me Gjeografinë, pasi distanca midis linjave të gjerësisë dhe gjatësisë është jo një distancë e barabartë veç Ndërsa i afroheni ekuatorit, linjat e gjerësisë gjeografike bëhen më larg. Nëse përdorni një lloj ekuacioni të thjeshtë trekëndëshi, ai mund të masë distancën me saktësi në një vend dhe tmerrësisht të gabuar në tjetrin, për shkak të lakimit të Tokës.

Formula Haversine

Harta e Fluturimit Evropë

Distanca duke përdorur lakimin e Tokës është e përfshirë në Formula e Haversine, e cila përdor trigonometrinë për të lejuar lakimin e tokës. Kur po gjeni distancën midis 2 vendeve në tokë (ndërsa fluturon sorra), një vijë e drejtë është me të vërtetë një hark.

Kjo është e zbatueshme në fluturimet ajrore - a keni shikuar ndonjëherë në hartën aktuale të fluturimeve dhe keni vërejtur se ato janë të harkuara? Kjo sepse është më e shkurtër të fluturosh në një hark midis dy pikave sesa direkt në vendndodhje.

PHP: Llogaritni distancën ndërmjet 2 pikëve

Gjithsesi, këtu është formula e PHP për llogaritjen e distancës midis dy pikave (së bashku me konvertimin Mile vs Kilometër) të rrumbullakosura në dy vende dhjetore.

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: Marrja e të gjitha rekordeve brenda një diapazoni duke llogaritur distancën në milje duke përdorur gjatësinë dhe gjatësinë

Alsoshtë gjithashtu e mundur të përdorni SQL për të bërë një llogaritje për të gjetur të gjitha rekordet brenda një distance të caktuar. Në këtë shembull, unë jam duke shkuar për të kërkuar MyTable në MySQL për të gjetur të gjitha rekordet që janë më pak ose të barabartë me ndryshueshme $ distancë (në Miles) në vendndodhjen time në $ gjerësi dhe $ gjatësi:

Kërkesa për marrjen e të gjitha rekordeve brenda një specifik distancë duke llogaritur distancën në milje midis dy pikave të gjerësisë dhe gjatësisë janë:

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

Do të duhet ta personalizoni këtë:

  • gjatësi $ - kjo është një variabël PHP ku po kaloj gjatësinë e pikës.
  • gjerësia $ - kjo është një variabël PHP ku po kaloj gjatësinë e pikës.
  • distanca $ - kjo është distanca që dëshironi të gjeni të gjitha rekordet më pak ose të barabarta.
  • tryezë - kjo është tabela ... ju do të dëshironi ta zëvendësoni atë me emrin tuaj të tabelës.
  • gjerësi - kjo është fusha e gjerësisë suaj gjeografike.
  • gjatësi - kjo është fusha e gjatësisë suaj.

SQL: Marrja e të gjitha rekordeve brenda një diapazoni duke llogaritur distancën në kilometra duke përdorur gjatësinë dhe gjatësinë

Dhe këtu është pyetja SQL duke përdorur kilometra në 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."

Do të duhet ta personalizoni këtë:

  • gjatësi $ - kjo është një variabël PHP ku po kaloj gjatësinë e pikës.
  • gjerësia $ - kjo është një variabël PHP ku po kaloj gjatësinë e pikës.
  • distanca $ - kjo është distanca që dëshironi të gjeni të gjitha rekordet më pak ose të barabarta.
  • tryezë - kjo është tabela ... ju do të dëshironi ta zëvendësoni atë me emrin tuaj të tabelës.
  • gjerësi - kjo është fusha e gjerësisë suaj gjeografike.
  • gjatësi - kjo është fusha e gjatësisë suaj.

Unë e shfrytëzova këtë kod në një platformë të hartëzimit të ndërmarrjeve që e shfrytëzuam për një dyqan me pakicë me mbi 1,000 vende në të gjithë Amerikën e Veriut dhe funksionoi bukur.

76 Comments

  1. 1

    Faleminderit shumë për ndarjen. Kjo ishte një punë e thjeshtë kopjimi dhe ngjitëse dhe funksionon shumë mirë. Ju më keni kursyer shumë kohë.
    FYI për cilindo që transporton në C:
    dyfish deg2rad (dyfish deg) {kthim deg * (3.14159265358979323846 / 180.0); }

  2. 2

    Një pjesë shumë e bukur e postimit - funksionoi shumë bukur - më duhej vetëm të ndryshoja emrin e tryezës që mbante gjatë. Ajo funksionon shumë shpejt për të. Unë kam një numër të arsyeshëm të vogël të gjatësive të gjata (<400) por mendoj se kjo do të shkojë mirë. Edhe faqe e bukur - sapo e kam shtuar në llogarinë time del.icio.us dhe do të kontrolloj rregullisht.

  3. 4
  4. 5
  5. 8

    Unë mendoj se SQL juaj ka nevojë për një deklaratë që ka.
    në vend të KU distancë <= $ distancë që mund t'ju duhet
    përdorni HAVING distance <= $ distancë

    përndryshe faleminderit që më kursyet një kohë dhe energji.

  6. 10
  7. 11
  8. 12

    Faleminderit shumë për ndarjen e këtij kodi. Më kurseu shumë kohë zhvillimi. Gjithashtu, falënderoj lexuesit tuaj për të theksuar se një deklaratë HAVING është e nevojshme për MySQL 5.x. Shume e dobishme.

  9. 14
  10. 15

    Përshëndetje,

    Një pyetje tjetër. A ka ndonjë formulë për vargjet NMEA si ajo më poshtë?

    1342.7500, N, 10052.2287, E

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

    Thanks,
    Harry

  11. 16

    Unë gjithashtu zbulova se KU nuk punonte për mua. E ndryshoi atë në HAVING dhe gjithçka funksionon në mënyrë perfekte. Në fillim nuk i lexova komentet dhe e rishkruaj atë duke përdorur një zgjedhje të vendosur. Të dy do të punojnë mirë.

  12. 17
  13. 18

    Tepër e dobishme, faleminderit shumë! Unë kisha disa probleme me "HAVING" të ri, në vend se "WHERE", por sapo lexova komentet këtu (pas rreth gjysmë ore kërcëllimë dhëmbët nga zhgënjimi = P), e kuptova duke punuar mirë. Faleminderit ^ _ ^

  14. 19
  15. 20

    Mbani në mend se një deklaratë e tillë do të jetë shumë e fortë nga ana kompjuterike dhe për këtë arsye e ngadaltë. Nëse keni shumë nga ato pyetje, kjo mund t'i shkatërrojë gjërat shumë shpejt.

    Një qasje shumë më pak intensive është të ekzekutohet një zgjedhje e parë (e papërpunuar) duke përdorur një zonë SQUARE të përcaktuar nga një distancë e llogaritur dmth. "Zgjidhni * nga emri i tabelës ku gjerësia midis lat1 dhe lat2 dhe gjatësia midis lon1 dhe lon2". lat1 = targetlatitude - latdiff, lat2 = targetlatitude + latdiff, ngjashëm me lon. latdiff ~ = distancë / 111 (për km), ose distancë / 69 për milje pasi 1 shkallë e gjerësisë gjeografike është ~ 111 km (ndryshim i lehtë pasi toka është paksa ovale, por e mjaftueshme për këtë qëllim). londiff = distancë / (abs (cos (deg2rad (gjerësi)) * 111)) - ose 69 për milje (në të vërtetë mund të marrësh një katror pak më të madh në mënyrë që të marrësh parasysh ndryshimet). Pastaj merrni rezultatin e kësaj dhe ushqejeni atë në zgjedhjen radiale. Thjesht mos harroni të merrni llogari për koordinatat jashtë kufijve - dmth diapazoni i gjatësisë gjeografike të pranueshme është -180 deri +180 dhe diapazoni i gjerësisë gjeografike të pranueshme është -90 deri +90 - në rast se latdiff ose londiff juaj shkon jashtë këtij kufiri . Vini re se në shumicën e rasteve kjo mund të mos jetë e zbatueshme pasi ndikon vetëm në llogaritjet mbi një vijë përmes oqeanit Paqësor nga poli në pol, megjithëse kryqëzon një pjesë të chukotkës dhe një pjesë të Alaskës.

    Ajo që ne arrijmë me këtë është një zvogëlim i ndjeshëm i numrit të pikëve kundrejt të cilave ju bëni këtë llogaritje. Nëse keni një milion pikë globale në bazën e të dhënave të shpërndara afërsisht në mënyrë të barabartë dhe dëshironi të kërkoni brenda 100 km, atëherë kërkimi juaj i parë (i shpejtë) është i një zone 10000 km katrore dhe ndoshta do të japë rreth 20 rezultate (bazuar në shpërndarjen e barabartë mbi një sipërfaqe prej rreth 500 milion km katrorë), që do të thotë që ju të kryeni llogaritjen komplekse të distancës 20 herë për këtë pyetje në vend të një milion herë.

    • 21
      • 22

        Këshilla fantastike! Unë në të vërtetë kam punuar me një zhvillues i cili shkruajti një funksion që tërhiqte sheshin brenda dhe pastaj një funksion rekursiv që bëri 'sheshet' rreth perimetrit për të përfshirë dhe përjashtuar pikat e mbetura. Rezultati ishte një rezultat tepër i shpejtë - ai mund të vlerësonte miliona pikë në mikrosekonda.

        Qasja ime më lart është padyshim 'e papërpunuar' por e aftë. Faleminderit perseri!

        • 23

          Doug,

          Unë kam qenë duke u përpjekur të përdor mysql dhe php për të vlerësuar nëse një pikë e gjatë lat është brenda një poligoni. A e dini nëse shoku juaj zhvillues ka botuar ndonjë shembull se si ta përmbushni këtë detyrë. Apo dini ndonjë shembull të mirë. Faleminderit paraprakisht

  16. 24

    Përshëndetje të gjithëve kjo është deklarata ime e testit SQL:

    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

    dhe Mysql po më thotë se distanca, nuk ekziston si kolonë, unë mund ta përdor porosinë, mund ta bëj pa KU, dhe funksionon, por jo me të

  17. 26

    Kjo është e mrekullueshme, megjithatë është ashtu si zogjtë fluturojnë. Do të ishte mirë të provosh dhe të përfshish API-të e hartave google në këtë farë mënyre (ndoshta duke përdorur rrugë etj.) Vetëm për të dhënë një ide duke përdorur një formë tjetër të transportit. Unë ende nuk kam bërë ende një funksion të simuluar të pjekjes në PHP që do të ishte në gjendje të ofrojë një zgjidhje efikase për problemin e shitësit udhëtues. Por mendoj se mund të jem në gjendje të ripërdor disa nga kodet tuaja për ta bërë këtë.

  18. 27
  19. 28

    Artikull i mirë! Gjeta shumë artikuj që përshkruanin se si të llogaritja distancën midis dy pikave, por vërtet po kërkoja fragmentin SQL.

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

    2 ditë kërkime për të gjetur më në fund këtë faqe që zgjidh problemin tim. Duket sikur më mirë të heq WolframAlpha tim dhe të pastroj matematikën time. Ndryshimi nga WHERE në HAVING e ka skenarin tim në gjendje pune. FALEMINDERIT

  25. 37
    • 38
  26. 39

    Unë uroj që kjo të ishte faqja e parë që do të kisha gjetur në këtë. Pasi provova shumë komanda të ndryshme, kjo ishte e vetmja që punoi si duhet, dhe me ndryshime minimale të nevojshme për të përshtatur bazën e të dhënave time.
    Thanks a lot!

  27. 40

    Unë uroj që kjo të ishte faqja e parë që do të kisha gjetur në këtë. Pasi provova shumë komanda të ndryshme, kjo ishte e vetmja që punoi si duhet, dhe me ndryshime minimale të nevojshme për të përshtatur bazën e të dhënave time.
    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

    faleminderit për postimin e këtij artikulli të dobishëm,  
    por për ndonjë arsye do të doja të pyesja
    si të merrni distancën midis koordinatave brenda mysql db dhe koordinatave të futura në php nga përdoruesi?
    për të përshkruar më qartë:
    1. përdoruesi duhet të fusë [id] për zgjedhjen e të dhënave të specifikuara nga db dhe koordinatat e vetë përdoruesit
    2. skedari php merr të dhënat e synuara (koordinatat) duke përdorur [id] dhe pastaj llogarit distancën midis përdoruesit dhe pikës së synuar

    apo thjesht mund të marrësh distancë nga kodi më poshtë?

    $ qry = "SELECT *, (((acos (sin ((". $ latitude. "* pi () / 180)) * sin ((" Latitude "* pi () / 180)) + cos ((". $ gjerësi. "* pi () / 180)) * cos ((" Latitude "* pi () / 180)) * cos (((". $ gjatësi. "-" Gjatësi ") * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) si distancë NGA "MyTable" WHERE distanca> = ". $ Distanca." >>>> a mund ta "heq" distancën nga këtu?
    faleminderit perseri,
    Timmy S

  41. 60

    ok, gjithçka që kam provuar nuk po funksionon. Dua të them, ajo që kam funksionon, por distancat janë larg.

    Dikush mund të shohë se çfarë është në rregull me këtë kod?

    nëse (lëshuesi ($ _ POST ['dorëzuar']))) {$ z = $ _POST ['kodi zip']; $ r = $ _POST ['rrezja']; jehonë "Rezultatet për". $ z; $ sql = mysql_query ("SELECT DISTINCT m.zipcode, m.MktName, m.LocAddSt, m.LocAddCity, m.LocAddState, m.x1, m.y1, m.verifikuar, z1.lat, z2.lon, z1. qytet, z1.state NGA mrk m, zip z1, zip z2 KU m.zipcode = z1.zipcode DHE z2.zipcode = $ z AND (3963 * acos (cung (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 ") ose vdes (mysql_error ()); ndërsa ($ row = mysql_fetch_array ($ sql)) {$ store1 = $ row ['MktName']. ""; $ store = $ row ['LocAddSt']. ""; $ store. = $ row ['LocAddCity']. ",". $ row ['LocAddState']. " ". $ Row ['kodi zip']; $ latitude1 = $ row ['lat']; $ gjatësia1 = $ rreshti ['lon']; $ latitude2 = $ rresht ['y1']; $ gjatësia2 = $ rreshti ['x1']; $ qyteti = $ rreshti ['qyteti']; $ state = $ row ['state']; $ dis = marrje e re ($ gjerësia1, $ gjatësia1, $ gjerësia2, $ gjatësia2, $ njësia = 'Mi'); // $ dis = distanca ($ lat1, $ lon1, $ lat2, $ lon2); $ verifikuar = $ row ['verifikuar']; nëse ($ verifikuar == '1') {jehonë ""; jehona "". dyqan $. ""; jehonë $ dis. " milje larg"; jehona ""; } other {echo “”. $ store. ””; jehonë $ dis. " milje larg"; jehona ""; }}}

    funksionet e mia.kodi php
    funksioni getnew ($ gjerësia 1, $ gjatësia1, $ gjerësia2, $ gjatësia2, $ njësia = 'Mi') {$ theta = $ gjatësia1 - $ gjatësia2; distanca $ = (sin (deg2rad ($ latitude1)) * sin (deg2rad ($ latitude2))) + (cos (deg2rad ($ latitude1)) * cos (deg2rad ($ latitude2)) * cos (deg2rad ($ theta)) ); $ distanca = akos ($ distancë); distanca $ = rad2deg ($ distanca); largësia $ = distanca $ * 60 * 1.1515; ndërprerësi ($ unit) {rasti 'Mi': break; rasti 'Km': $ distanca = $ distanca * 1.609344; } kthimi (raundi ($ distanca, 2)); }

    Thank you in advance

  42. 61
  43. 62

    Hej Douglas, artikull i shkëlqyeshëm. Kam gjetur shpjegimin tuaj për konceptet gjeografike dhe kodin me të vërtetë interesant. Sugjerimi im i vetëm do të ishte hapësira dhe prerja e kodit për shfaqje (si Stackoverflow, për shembull). Unë e kuptoj që ju doni të kurseni hapësirë, por ndarja konvencionale e kodit / dhëmbëzimi do ta bënte shumë më të lehtë për mua, si një programues, leximin dhe zbërthimin. Sidoqoftë, kjo është një gjë e vogël. Vazhdoni punën e madhe.

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

    duket më shpejt (mysql 5.9) të përdorësh dy herë formulën në zgjedhje dhe ku:
    formula $ = "(((acos (sin ((". $ gjerësi. "* pi () / 180)) * sin ((" Latitude "* pi () / 180)) + cos ((". $ gjerësi. "* Pi () / 180)) * cos ((" Latitude "* pi () / 180)) * cos (((". $ Gjatësi. "-" Gjatësi ") * pi () / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) ”;
    $ sql = 'SELECT *,'. $ formula. ' si distancë NGA tabela KU '.. formula $.' <= '. distanca $;

  51. 71
  52. 72

    Faleminderit shumë për prerjen e këtij artikulli. Është shumë e dobishme.
    PHP në fillim u krijua si një platformë e thjeshtë skriptuese e quajtur "Faqja Kryesore Personale". Në ditët e sotme PHP (shkurtesa për Hypertext Preprocessor) është një alternativë e teknologjisë Active Server Pages (ASP) të Microsoft.

    PHP është një gjuhë me server të hapur me burim të hapur e cila përdoret për krijimin e faqeve dinamike në internet. Mund të ngulitet në HTML. PHP zakonisht përdoret së bashku me një bazë të dhënash MySQL në serverat e internetit Linux / UNIX. Ndoshta është gjuha më e njohur e skenarit.

  53. 73

    Kam gjetur zgjidhjen e mësipërme që nuk funksionon si duhet.
    Unë kam nevojë për të ndryshuar në:

    $ qqq = "SELECT *, (((acos (sin ((". $ latitude. "* pi () / 180)) * sin ((` latt` * pi () / 180)) + cos ((". gjerësia $. "* pi () / 180)) * cos ((` latt` * pi () / 180)) * cos (((". $ gjatësi." - "longt") * pi () / 180) ))) * 180 / pi ()) * 60 * 1.1515) si distancë NGA `regjistri`;

  54. 75
  55. 76

    Përshëndetje, ju lutem do të më duhet vërtet ndihma juaj për këtë.

    Unë bëra një kërkesë për të marrë në serverin tim të internetit http://localhost:8000/users/findusers/53.47792/-2.23389/20/
    53.47792 = $ gjerësi
    -2.23389 = $ gjatësi
    dhe 20 = distanca që dua të rikuperoj

    Sidoqoftë duke përdorur formulën tuaj, ajo rigjen të gjitha rreshtat në db tim

    $ results = DB :: select (DB :: raw ("SELECT *, (((acos (sin ((". $ latitude. "* pi () / 180)) * sin ((lat * pi () / 180 )) + cos ((". $ gjerësi." * pi () / 180)) * cos ((lat * pi () / 180)) * cos (((". $ gjatësi." - lng) * pi ( ) / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) si distancë NGA shënuesit që KANI distancë> = ". $ Distancë));

    [{"Id": 1, "emri": "Frankie Johnnie & Luigo Too", "adresa": "939 W El Camino Real, Mountain View, CA", "lat": 37.386337280273, "lng": - 122.08582305908, ”Distanca”: 16079.294719663}, {“id”: 2, ”emri”: ”Piceri i Bregut Lindor të Amici”, ”adresa”: ”790 Castro St, Mountain View, CA”, ”lat”: 37.387138366699, ”lng”: -122.08323669434, ”distanca”: 16079.175940152}, {“id”: 3, ”emri”: ”Kapp's Pizza Bar & Grill”, ”adresa”: ”191 Castro St, Mountain View, CA”, ”lat”: 37.393886566162, ”Lng”: - 122.07891845703, ”distanca”: 16078.381373826}, {“id”: 4, ”emri”: ”Rruga e tryezës së rrumbullakët: Pamja malore”, ”adresa”: ”570 N Shoreline Blvd, Mountain View, CA”, ”Lat”: 37.402652740479, ”lng”: - 122.07935333252, ”distanca”: 16077.420540582}, {“id”: 5, ”emri”: ”Tony & Alba's Pizza & Pasta”, ”adresa”: ”Ave 619 Escuela, Mountain Pamje, CA ”,” lat ”: 37.394012451172,” lng ”: - 122.09552764893,” distancë ”: 16078.563225154}, {“ id ”: 6,” emër ”:” Pica e Oreganos me zjarr në dru ”,” adresa ”:” 4546 El Camino Real, Los Altos, CA ”,” lat ”: 37.401725769043,” lng ”: - 122.11464691162,” distanca ”: 16077.937560795}, {“ id ”: 7,” emri ”:” Baret dhe grilat ”,” adresa ”:” 24 Whiteley Street, Manchester ”,” lat ”: 53.485118865967,” lng ”: - 2.1828699111938,” distanca ”: 8038.7620112314}]

    Unë dua të marr vetëm rreshta me 20 milje, por kjo sjell të gjitha rreshtat. Ju lutem çfarë po bëj gabim

Çfarë mendoni ju?

Kjo faqe përdor Akismet për të reduktuar spamin. Mësoni se si përpunohet komenti juaj.