Пресметајте или побарајте големо растојание на кругот помеѓу точките на географска широчина и должина користејќи ја формулата Haversine (Примери PHP, Python, MySQL, MSSQL)

Формула Haversine - Големо растојание на кругот - PHP, Python, MySQL

Овој месец програмирав доста во PHP и MySQL во однос на ГИС. Душкајќи по мрежата, навистина ми беше тешко да најдам дел од Географски пресметки да најдам растојание помеѓу две локации, па затоа сакав да ги споделам тука.

Мапа на летови Европа со голема далечина на кругови

Едноставен начин за пресметување на растојание помеѓу две точки е користење на Питагоровата формула за пресметување на хипотенузата на триаголник (A² + B² = C²). Ова е познато како Евклидовска оддалеченост.

Тоа е интересен почеток, но не се однесува на географијата, бидејќи растојанието е помеѓу линиите на географска ширина и должина не еднакви растојанија разделени Како што се приближувате до екваторот, линиите на ширина се оддалечуваат. Ако користите некаква едноставна равенка на трианглацијата, може да се измери растојанието точно на една локација и страшно погрешно на другата, заради закривеноста на Земјата.

Големо растојание на кругот

Патеките што се патуваат на долги растојанија околу Земјата се познати како Големо растојание на кругот. Тоа е… најкраткото растојание помеѓу две точки на сфера е различно од точките на рамна карта. Комбинирајте го тоа со фактот дека линиите на географска ширина и должина не се еднакво оддалечени… и имате тешка пресметка.

Еве фантастично видео објаснување за тоа како функционираат Големите кругови.

Формулата Хаверсин

Растојанието со искривување на Земјата е вградено во Формула на аверсин, која користи тригонометрија за да овозможи искривување на земјата. Кога наоѓате растојание помеѓу 2 места на земјата (додека лета врана), права линија е навистина лак.

Ова се применува при летање со авион - дали некогаш сте ја погледнале вистинската мапа на летови и сте забележале дека се заоблени? Тоа е затоа што е пократко да се лета во лак помеѓу две точки отколку директно до локацијата.

PHP: Пресметајте го растојанието помеѓу 2 точки на географска ширина и должина

Еве ја PHP формулата за пресметување на растојанието помеѓу две точки (заедно со конверзија Миле наспроти Километар) заокружена на две децимални места.

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)); 
}

Променливите се:

  • $Latitude1 – променлива за географската ширина на вашата прва локација.
  • $ Географска должина1 – променлива за должината на вашата прва локација
  • $Latitude2 – променлива за географската ширина на вашата втора локација.
  • $ Географска должина2 – променлива за географската должина на вашата втора локација.
  • $ единица – стандардното битие милји. Ова може да се ажурира или пренесе како километри.

Python: Пресметајте растојание помеѓу 2 точки на географска ширина и должина

Како и да е, еве ја Python формулата за пресметување на растојанието помеѓу две точки (заедно со конверзија Миле наспроти Километар) заокружено на две децимални места. Заслуга за мојот син, Бил Кар, кој е научник за податоци OpenINSIGHTS, за кодот.

from numpy import sin, cos, arccos, pi, round

def rad2deg(radians):
    degrees = radians * 180 / pi
    return degrees

def deg2rad(degrees):
    radians = degrees * pi / 180
    return radians

def getDistanceBetweenPointsNew(latitude1, longitude1, latitude2, longitude2, unit = 'miles'):
    
    theta = longitude1 - longitude2
    
    distance = 60 * 1.1515 * rad2deg(
        arccos(
            (sin(deg2rad(latitude1)) * sin(deg2rad(latitude2))) + 
            (cos(deg2rad(latitude1)) * cos(deg2rad(latitude2)) * cos(deg2rad(theta)))
        )
    )
    
    if unit == 'miles':
        return round(distance, 2)
    if unit == 'kilometers':
        return round(distance * 1.609344, 2)

Променливите се:

  • географска ширина 1 – променлива за вашата прва локација географска ширина.
  • географска должина 1 – променлива за вашата прва локација должина
  • географска ширина 2 – променлива за вашата втора локација географска ширина.
  • географска должина 2 – променлива за вашата втора локација должина.
  • единица – стандардното битие милји. Ова може да се ажурира или пренесе како километри.

MySQL: Враќање на сите записи во опсег со пресметување на растојание во милји користејќи географска ширина и должина

Исто така е можно да се користи SQL за да се направи пресметка за да се најдат сите записи на одредено растојание. Во овој пример, ќе побарам MyTable во 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) as distance FROM `table` WHERE distance <= ".$distance."

Needе треба да го прилагодите ова:

  • должина $ - ова е PHP променлива каде што минувам по должината на точката.
  • $ географска ширина - ова е PHP променлива каде што минувам по должината на точката.
  • $ растојание - ова е растојанието што сакате да ги најдете сите записи помалку или еднакви.
  • маса - ова е табелата… ќе сакате да ја замените со името на вашата табела.
  • географска ширина - ова е полето на вашата географска ширина.
  • должина - ова е полето на вашата должина.

MySQL: Враќање на сите записи во опсег со пресметување на растојание во километри користејќи географска ширина и географска должина

И еве го прашањето SQL користејќи километри во 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."

Needе треба да го прилагодите ова:

  • должина $ - ова е PHP променлива каде што минувам по должината на точката.
  • $ географска ширина - ова е PHP променлива каде што минувам по должината на точката.
  • $ растојание - ова е растојанието што сакате да ги најдете сите записи помалку или еднакви.
  • маса - ова е табелата… ќе сакате да ја замените со името на вашата табела.
  • географска ширина - ова е полето на вашата географска ширина.
  • должина - ова е полето на вашата должина.

Јас го искористив овој код во платформа за мапирање на претпријатие што ја искористивме за продавница за малопродажба со над 1,000 локации низ Северна Америка и работеше прекрасно.

Географско растојание на Microsoft SQL Server: STDDistance

Ако користите Microsoft SQL Server, тие нудат своја сопствена функција, STD растојание за пресметување на растојанието помеѓу две точки со помош на типот на податоци Географија.

DECLARE @g geography;  
DECLARE @h geography;  
SET @g = geography::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656)', 4326);  
SET @h = geography::STGeomFromText('POINT(-122.34900 47.65100)', 4326);  
SELECT @g.STDistance(@h);  

Совет за капа до Манаш Саху, потпретседател и архитект на Highbridge.

77 Коментари

  1. 1

    Ви благодариме многу за споделувањето. Ова беше лесна работа за копирање и залепување и одлично функционира. Ти ми заштеди многу време.
    FYI за секој што пренесува во C:
    double deg2rad(double deg) { return deg*(3.14159265358979323846/180.0); }

  2. 2

    Многу убаво парче објава - работеше многу убаво - морав само да го сменам името на масата на која се наоѓа lat-long. Работи прилично брзо до.. Имам разумно мал број на lat-longs (< 400), но мислам дека ова би се размери убаво. Прекрасна страница исто така - штотуку ја додадов на мојата сметка на del.icio.us и редовно ќе проверувам.

  3. 4
  4. 5

    Цел ден барав пресметки за растојание и го најдов алгоритмот harversine, благодарение на тебе што го даде примерот како да го ставиш во sql изјава. Фала и поздрав, Даниел

  5. 8

    Мислам дека на вашиот SQL му треба изјава за поседување.
    наместо WHERE растојание <= $distance можеби ќе треба
    користете HAVING растојание <= $distance

    инаку благодарам што ми заштедивте многу време и енергија.

  6. 10
  7. 11
  8. 12

    Ви благодариме многу што го споделивте овој код. Тоа ми заштеди многу време за развој. Исто така, благодарност до вашите читатели што посочија дека изјавата HAVING е неопходна за MySQL 5.x. Многу помага.

  9. 14

    Горенаведената формула ми заштедува многу време. Ти благодарам многу.
    Исто така, треба да се префрлам помеѓу форматот NMEA и Степени. Најдов формула на оваа URL на дното на страницата. http://www.errorforum.com/knowledge-base/16273-converting-nmea-sentence-latitude-longitude-decimal-degrees.html

    Дали некој знае како да го потврди ова?

    Ви благодариме!
    Хари

  10. 15
  11. 16

    Открив и дека WHERE не ми делуваше. Го сменив во HAVING и се работи совршено. Отпрвин не ги прочитав коментарите и ги препишав користејќи вгнездено избирање. И двете ќе работат добро.

  12. 17

    Ви благодарам многу за скриптата напишана во mysql, само требаше да направам неколку мали прилагодувања (ИМАТЕ) 🙂
    Одлична работа

  13. 18

    Неверојатно корисно, ви благодарам многу! Имав некои проблеми со новото „HAVING“, наместо „WHERE“, но штом ги прочитав коментарите овде (по околу половина час чкртање заби од фрустрација =P), сфатив дека функционира добро. Ви благодариме ^_^

  14. 19
  15. 20

    Имајте на ум дека избраната изјава како таа ќе биде многу пресметковна интензивна и затоа бавна. Ако имате многу од тие прашања, може многу брзо да ги заглави работите.

    Многу помалку интензивен пристап е да се изврши првиот (суров) избор со користење на КВАДРАТНА област дефинирана со пресметано растојание т.е. „избери * од името на табелата каде географската ширина помеѓу lat1 и lat2 и должината помеѓу lon1 и lon2“. lat1 = целна ширина – latdiff, lat2 = целна ширина + latdiff, слично со лон. latdiff ~= растојание / 111 (за km), или растојание/69 за милји бидејќи 1 степен на географска ширина е ~ 111 km (мала варијација бидејќи земјата е малку овална, но доволна за оваа намена). londiff = растојание / (abs(cos(deg2rad(географска ширина))*111)) - или 69 за милји (всушност можете да земете малку поголем квадрат за да ги земете предвид варијациите). Потоа земете го резултатот од тоа и внесете го во радијалниот избор. Само не заборавајте да ги земете предвид координатите надвор од границите - т.е. опсегот на прифатлива географска должина е -180 до +180 и опсегот на прифатлива географска ширина е -90 до +90 - во случај вашиот latdiff или londiff да тече надвор од овој опсег . Забележете дека во повеќето случаи ова можеби не е применливо бидејќи влијае само на пресметките преку линијата низ Тихиот океан од пол до пол, иако пресекува дел од чукотка и дел од Алјаска.

    Она што го постигнуваме со ова е значително намалување на бројот на поени според кои ја правите оваа пресметка. Ако имате милион глобални точки во базата распределени приближно рамномерно и сакате да пребарувате во рок од 100 km, тогаш вашето прво (брзо) пребарување е на површина од 10000 квадратни километри и веројатно ќе даде околу 20 резултати (врз основа на рамномерна распределба на површина од околу 500 милиони квадратни километри), што значи дека ја извршувате сложената пресметка на растојанието 20 пати за ова барање наместо милион пати.

    • 21

      Мала грешка во примерот... тоа би било за 50 км (не 100) бидејќи го гледаме „радиусот“ на нашиот... квадрат.

      • 22

        Фантастичен совет! Јас всушност работев со програмер кој напиша функција која го повлекува внатрешниот квадрат, а потоа рекурзивна функција која прави „квадрати“ околу периметарот за да ги вклучи и исклучи преостанатите точки. Резултатот беше неверојатно брз резултат - тој можеше да оцени милиони поени во микросекунди.

        Мојот пристап погоре е дефинитивно „груб“, но способен. Благодарам уште еднаш!

        • 23

          Даг,

          Се обидував да користам mysql и php за да проценам дали лат долгата точка е во полигон. Дали знаете дали вашиот пријател програмер објави примери за тоа како да ја постигнете оваа задача. Или знаете некои добри примери. Благодарам однапред.

  16. 24

    Здраво на сите, ова е мојата тест 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

    и Mysql ми кажува дека растојанието, не постои како колона, можам да користам редослед според, можам да го направам тоа без WHERE, и работи, но не со него…

  17. 26

    Ова е одлично, но е исто како што летаат птиците. Би било одлично да се обидете да го вклучите API-то на google maps на ова некако (можеби користејќи патишта итн.) Само за да дадете идеја користејќи различна форма на транспорт. Сè уште треба да направам симулирана функција за жарење во PHP која би можела да понуди ефикасно решение за проблемот со патувачкиот продавач. Но, мислам дека можеби ќе можам повторно да користам дел од вашиот код за да го сторам тоа.

  18. 27

    Здраво Даглас,
    Ви благодарам многу за оваа статија - само што ми заштедивте многу време.
    чувај се,
    nimrod @Израел

  19. 28

    Добра статија! Најдов многу статии кои опишуваат како да се пресмета растојанието помеѓу две точки, но навистина го барав фрагментот SQL.

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

    2 дена истражување за конечно да ја најдам оваа страница што ми го решава проблемот. Изгледа дека е подобро да го отфрлам мојот WolframAlpha и да се осврнам на мојата математика. Промената од WHERE во HAVING го има моето сценарио во работна состојба. ВИ БЛАГОДАРАМ

  25. 37
  26. 39

    Посакувам ова да беше првата страница што ја најдов на ова. Откако пробав многу различни команди, оваа беше единствената што функционираше правилно, и со минимални промени потребни за да се вклопи во мојата сопствена база на податоци.
    Фала многу!

  27. 40

    Посакувам ова да беше првата страница што ја најдов на ова. Откако пробав многу различни команди, оваа беше единствената што функционираше правилно, и со минимални промени потребни за да се вклопи во мојата сопствена база на податоци.
    Фала многу!

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

    Знам дека оваа формула функционира, но не можам да видам каде се зема предвид радиусот на земјата. Може ли некој да ме просветли, ве молам?

  34. 49
  35. 50
  36. 52
  37. 53

    Постојано добивам Порака за грешка: Непозната колона „Растојание“ во „клаузулата каде“ на барањето за MySQL.

  38. 55
  39. 56
  40. 58

    благодариме што ја објавивте оваа корисна статија,  
    но поради некоја причина би сакал да прашам
    како да се добие растојанието помеѓу координите во mysql db и координите вметнати во php од корисникот?
    за појасно опишете:
    1.корисникот треба да вметне [id] за избирање на одредени податоци од db и координатите на самиот корисник
    2. php-датотеката ги добива целните податоци (коорди) користејќи [id] и потоа пресметајте го растојанието помеѓу корисникот и целната точка

    или едноставно може да се оддалечи од кодот подолу?

    $qry = „SELECT *,(((acos(sin((“.$latitude."*pi()/180)) * sin((`Latitude`*pi()/180))+cos((“. $широчина."*pi()/180)) * cos((`Широчина`*pi()/180)) * cos(((".$лонгитуда."- `Гоградина`)*pi()/180) )))*180/pi())*60*1.1515*1.609344) како растојание ОД „Моја табела“ WHERE растојание >= „.$distance“. >>>>можам ли да го „извадам“ растојанието од овде?
    благодарам уште еднаш,
    Тими С

  41. 60

    ок, се што пробав не работи. Мислам, тоа што го имам функционира, но далечините се далеку.

    Дали некој може да види што не е во ред со овој код?

    if(isset($_POST['поднесено'])){ $z = $_POST['поштенски код']; $r = $_POST['радиус']; ехо „Резултати за „.$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. град,z1.state FROM mrk m, zip z1, zip z2 WHERE m.zipcode = z1.zipcode И 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) или die (mysql_error()); while($row = mysql_fetch_array($sql)) {$store1 = $row['MktName'].""; $store = $row['LocAddSt'].””; $store .= $row['LocAddCity'].", ".$row['LocAddState']." „.$row['zipcode']; $latitude1 = $ред['lat']; $longitude1 = $ред['лон']; $latitude2 = $ред['y1']; $longitude2 = $ред['x1']; $city = $ред['град']; $state = $ред['состојба']; $dis = getnew($latitude1, $longitude1, $latitude2, $longitude2, $unit = 'Mi'); // $dis = растојание ($lat1, $lon1, $lat2, $lon2); $verified = $ред['потврдено']; if($verified == '1'){ ехо „“; ехо “”.$store.””; ехо $dis . „милја(и) подалеку“; ехо „“; } else { echo „“.$store.““; ехо $dis . „милја(и) подалеку“; ехо „“; } }}

    моите функции.php код
    функцијата getnew($latitude1, $longitude1, $latitude2, $longitude2, $unit = 'Mi') { $theta = $longitude1 – $longitude2; $растојание = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))) + (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta)) ); $distance = acos($distance); $растојание = rad2deg($растојание); $distance = $distance * 60 * 1.1515; switch($unit) { case 'Mi': break; случај 'Km' : $distance = $distance * 1.609344; } враќање (круг ($растојание,2)); }

    Благодарам однапред

  42. 61
  43. 62

    Здраво Даглас, одлична статија. Навистина ми беше интересно твоето објаснување за географските поими и кодот. Мојот единствен предлог би бил да се стави празно место и да се вовлече кодот за прикажување (како Stackoverflow, на пример). Разбирам дека сакате да заштедите простор, но конвенционалното растојание / вовлекување на кодот многу би ми олеснило, како програмер, да читам и сецирам. Како и да е, тоа е мала работа. Продолжете со одличната работа.

  44. 64
  45. 65

    овде додека користиме со функција добиваме еден вид растојание .. додека користиме пребарување што доаѓа неговиот друг вид на растојание

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

    се чини побрзо (mysql 5.9) да се користи двапати формула при изборот и каде:
    $формула = „(((acos(sin((“.$ ширина.“*pi()/180)) * sin((`Широчина`*pi()/180))+cos((“.$ ширина. ”*pi()/180)) * cos((`Широчина`*pi()/180) *180/pi())*180*60*1.1515)”;
    $sql = 'SELECT *, '.$формула.' како растојание од табелата КАДЕ „..$формула.“ <= '.$distance;

  51. 71
  52. 72

    Ви благодариме многу за стрижењето на овој напис. многу е корисен.
    PHP најпрво беше создаден како едноставна платформа за скриптирање наречена „Лична почетна страница“. Во денешно време PHP (кратенката за Hypertext Preprocessor) е алтернатива на технологијата на Microsoft Active Server Pages (ASP) технологија.

    PHP е јазик со отворен код од страна на серверот кој се користи за креирање динамични веб-страници. Може да се вметне во HTML. PHP обично се користи заедно со базата на податоци MySQL на веб-серверите на Linux/UNIX. Тоа е веројатно најпопуларниот јазик за скриптирање.

  53. 73

    Сфатив дека решението погоре не работи правилно.
    Треба да се сменам на:

    $qqq = „SELECT *,(((acos(sin((“.$latitude.”*pi()/180)) * sin((`latt`*pi()/180))+cos((“ . $ географска ширина . „*pi()/180)) * cos((`latt`*pi()/180)) * cos(((“ . $longitude . „- `longt`)*pi()/180) )))*180/pi())*60*1.1515) како растојание ОД `регистар`“;

  54. 75
  55. 76

    Здраво, ве молам, навистина ќе ми треба вашата помош за ова.

    Направив барање за добивање до мојот веб-сервер http://localhost:8000/users/findusers/53.47792/-2.23389/20/
    53.47792 = $ ширина
    -2.23389 = $ должина
    и 20 = растојанието што сакам да го повлечам

    Како и да е, користејќи ја формулата, ги враќа сите редови во мојата db

    $results = DB::select( DB::raw("SELECT *, (((acos(sin((".$latitude."*pi()/180)) * sin((lat*pi()/180 ))+cos ((„.$ ширина.“*pi()/180)) * cos((lat*pi()/180)) * cos(((„$ложина."- lng)*pi( ()

    [{“id”:1,”име”:”Frankie Johnnie & Luigo Too”,”адреса”:”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", "адреса": "790 Castro Street, Mountain View, CA","lat":37.387138366699,"lng": -122.08323669434,”distance”:16079.175940152},{“id”:3,”name”:”Kapp's Pizza Bar & Grill”,”адреса”:”191 Castro Street, Mountain View, CA”,”lat”:37.393886566162, “lng”:-122.07891845703,”distance”:16078.381373826},{“id”:4,”name”:”Пица на тркалезна маса: Mountain View”,”адреса”:”570 N Shoreline Blvd, Mountain View, CA”, "lat":37.402652740479,"lng":-122.07935333252,"distance":16077.420540582},{"id":5,"име":"Tony & Alba's Pizza & Pasta","адреса на планината A619": View, CA","lat":37.394012451172,"lng":-122.09552764893,"distance":16078.563225154},{"id":6,"name":"Oregano's Wood-reed Pizza"4546",37.401725769043" Ел Камино Реал, Лос Алтос, Калифорнија","лат":122.11464691162,"lng":-16077.937560795," distance":7},{" id”:24,”име”:”Бала и скари”,”адреса”:”Улица Вајтли 53.485118865967, Манчестер”,”лат”:2.1828699111938,”lng”:-8038.7620112314,”дистанца”:XNUMX]]

    Сакам да повратам само редови со 20 милји, но ги носи сите редови. Ве молам, што правам погрешно

  56. 77

    Барам слично барање, но малку се засилив - накратко, ова е да се групираат сите координати во рок од 2 милји од секоја координата, а потоа да се брои колку координати во секоја група и да се излезе само една група која има најмногу координати - дури и ако имате повеќе од една група меѓу групите кои имаат најголем број на координати - едноставно извадете ја случајната група од групите со ист најголем број -

Што мислите?

Оваа страница користи Akismet за намалување на спам. Научете како се обработува вашиот коментар.