Обчисліть відстань між двома точками широти-довготи? (Формула Гаверсіна)


905

Як обчислити відстань між двома точками, вказаними широтою та довготою?

Для уточнення я хотів би відстань у кілометрах; бали використовують систему WGS84, і я хотів би зрозуміти відносну точність наявних підходів.


Для кращої точності - див. Stackoverflow.com/questions/1420045/…
Lior Kogan

3
Зауважте, що ви не можете застосувати формулу Гаверсіна до еліпсоїда обертання, як WGS 84. Ви можете застосувати цей метод лише у сфері з радіусом.
Майк Т

3
Більшість відповідей тут використовують просту сферичну тригонометрію, тому результати є досить грубими порівняно з еліпсоїдними відстанями WGS84, що використовуються в системі GPS. Деякі відповіді стосуються формули Вінсенті для еліпсоїдів, але цей алгоритм був розроблений для використання на настільних калькуляторах епохи 1960-х, і він має проблеми зі стабільністю та точністю; зараз у нас є краще апаратне та програмне забезпечення. Будь ласка, перегляньте GeographicLib для високоякісної бібліотеки з реалізаціями на різних мовах.
PM 2Кривень

@MikeT - правда, хоча багато відповідей тут здаються корисними на невеликих відстанях : якщо ви берете lat / long від WGS 84 і застосовуєте Haversine так, ніби це точки на кулі, ви не отримуєте відповіді, помилки яких лише через коефіцієнт сплющення Землі, тож можливо в межах 1% від більш точної формули? З застереженням, що це невеликі відстані, скажімо, в межах одного міста.
ToolmakerSteve

1
Для цих форм: Mono / .NET 4.5 / .NET Core / Windows Phone 8.x / Universal Windows Platform / Xamarin iOS / Xamarin Android див. Stackoverflow.com/a/54296314/2736742
А. Морель

Відповіді:


1145

Це посилання може бути корисним для вас, оскільки воно детально використовує формулу Хаверсіна для обчислення відстані.

Витяг:

Цей скрипт [у Javascript] обчислює відстані великого кола між двома точками - тобто найменшою відстані над земною поверхнею - за допомогою формули «Гаверсина».

function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; // Distance in km
  return d;
}

function deg2rad(deg) {
  return deg * (Math.PI/180)
}

51
Чи враховує цей розрахунок / метод, що Земля є сфероїдом (не ідеальною сферою)? В оригінальному запитанні було задано відстань між точками на глобусі WGS84. Не впевнений, скільки помилок стикається за допомогою ідеальної сфери, але я підозрюю, що це може бути досить багато залежно від того, де точки на земній кулі, тому різницю варто пам’ятати.
redcalx

15
Формула Гаверсіна не враховує, що Земля є сфероїдом, тому ви отримаєте певну помилку через цей факт. Це не може бути гарантовано правильним до 0,5%. Це, можливо, не може бути прийнятним рівнем помилок.
Брендон

24
Чи є якась причина Math.atan2(Math.sqrt(a), Math.sqrt(1-a))замість цього використовувати Math.asin(Math.sqrt(h)), яка була б прямою реалізацією формули, яку використовує стаття у Вікіпедії? Це більш ефективно та / або більш стабільно чисельно?
musiphil

16
@UsmanMutawakil Ну, 38 миль, які ви отримуєте, - це відстань на дорозі. Цей алгоритм обчислює прямолінійну відстань на земній поверхні. Карти Google мають інструмент відстані (зліва внизу, "Лабораторії"), який робить те саме, використовуйте його для порівняння.
Паскаль

4
@ Forte_201092: Тому що не треба - а (sin(x))²так само(sin(-x))²
Жан Hominal

359

Мені потрібно було обчислити велику відстань між точками для мого проекту, тому я пішов вперед і спробував оптимізувати код, який я знайшов тут. В середньому в різних браузерах моя нова реалізація працює в 2 рази швидше, ніж відповідь, що найбільше підтримується.

function distance(lat1, lon1, lat2, lon2) {
  var p = 0.017453292519943295;    // Math.PI / 180
  var c = Math.cos;
  var a = 0.5 - c((lat2 - lat1) * p)/2 + 
          c(lat1 * p) * c(lat2 * p) * 
          (1 - c((lon2 - lon1) * p))/2;

  return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}

Ви можете пограти з моїм jsPerf і переглянути результати тут .

Нещодавно мені потрібно було зробити те ж саме в python, ось ось реалізація python :

from math import cos, asin, sqrt, pi

def distance(lat1, lon1, lat2, lon2):
    p = pi/180
    a = 0.5 - cos((lat2-lat1)*p)/2 + cos(lat1*p) * cos(lat2*p) * (1-cos((lon2-lon1)*p))/2
    return 12742 * asin(sqrt(a)) #2*R*asin...

І заради повноти: Гаверсіне на вікі.


13
@AngularM і існує велика ймовірність, що Google розраховує відстань, якщо ви будете їхати по деяких дорогах, а не по прямій.
Сальвадор Далі

3
Google обчислює відстань їзди, це обчислює "як ворона летить"
Хоббіст

4
@Ouadie і чи поліпшить це швидкість? Найімовірніше, ні, але я закінчу багато "ваших речей не працює" для людей, які копіюють його в старих браузерах
Сальвадор Далі

4
ну так, але що // 2 * R; R = 6371 kmозначає? і поточний метод забезпечує відповідь у км або милях? потребує кращої документації. Спасибі
Халіл Халаф

20
@KhalilKhalaf ти тут жартуєш чи намагаєшся троліти? км стоїть на кілометри. Як ви вважаєте, що означає R (особливо якщо ми говоримо про шперс)? Вгадайте, в яких одиницях буде відповідь, якщо ви вже бачите км. Яку документацію ви шукаєте тут: там буквально 4 рядки.
Сальвадор Далі

69

Ось реалізація C #:

static class DistanceAlgorithm
{
    const double PIx = 3.141592653589793;
    const double RADIUS = 6378.16;

    /// <summary>
    /// Convert degrees to Radians
    /// </summary>
    /// <param name="x">Degrees</param>
    /// <returns>The equivalent in radians</returns>
    public static double Radians(double x)
    {
        return x * PIx / 180;
    }

    /// <summary>
    /// Calculate the distance between two places.
    /// </summary>
    /// <param name="lon1"></param>
    /// <param name="lat1"></param>
    /// <param name="lon2"></param>
    /// <param name="lat2"></param>
    /// <returns></returns>
    public static double DistanceBetweenPlaces(
        double lon1,
        double lat1,
        double lon2,
        double lat2)
    {
        double dlon = Radians(lon2 - lon1);
        double dlat = Radians(lat2 - lat1);

        double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2));
        double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
        return angle * RADIUS;
    }

}

14
Ви використовуєте екваторіальний радіус, але вам слід скористатися середнім радіусом, який становить 6371 км
Філіп Лейбаерт

7
Хіба це не повинно бути double dlon = Radians(lon2 - lon1);іdouble dlat = Radians(lat2 - lat1);
Кріс Марісіч

Я згоден з Крісом Марисічем. Я використовував оригінальний код, і розрахунки були неправильними. Я додав заклик перетворити дельти в радіани, і він справно працює. Я подав правку і чекаю, коли вона буде перевірена.
Брайан Бедард

Я подав ще одну редакцію, оскільки lat1 та lat2 також потрібно перетворити на радіани. Я також переглянув формулу для присвоєння формулі та коду, який можна знайти тут: movable-type.co.uk/scripts/latlong.html
Брайан Бедард

чи RADIUSмає бути значення 6371, як і в інших відповідях?
Кріс Хейс

66

Ось реалізація Java формули Хаверсіна.

public final static double AVERAGE_RADIUS_OF_EARTH_KM = 6371;
public int calculateDistanceInKilometer(double userLat, double userLng,
  double venueLat, double venueLng) {

    double latDistance = Math.toRadians(userLat - venueLat);
    double lngDistance = Math.toRadians(userLng - venueLng);

    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
      + Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(venueLat))
      * Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2);

    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return (int) (Math.round(AVERAGE_RADIUS_OF_EARTH_KM * c));
}

Зауважимо, що тут ми округляємо відповідь до найближчих км.


2
Якби ми хотіли порахувати відстань між двома точками в метрах, який би точніший спосіб? Використовувати 6371000як радіус землі? (середній радіус землі - 6371000 метрів) або перетворити кілометри в метри від вашої функції?
Мікро

якщо ви хочете миль, результат 0.621371
помножте

42

Дуже дякую за все це. Я використовував наступний код у своєму додатку iPhone від Objective-C:

const double PIx = 3.141592653589793;
const double RADIO = 6371; // Mean radius of Earth in Km

double convertToRadians(double val) {

   return val * PIx / 180;
}

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

        double dlon = convertToRadians(place2.longitude - place1.longitude);
        double dlat = convertToRadians(place2.latitude - place1.latitude);

        double a = ( pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))) * cos(convertToRadians(place2.latitude)) * pow(sin(dlon / 2), 2);
        double angle = 2 * asin(sqrt(a));

        return angle * RADIO;
}

Широта та Довгота знаходяться в десяткових числах. Я не використовував min () для дзвінка asin (), оскільки відстані, які я використовую, настільки малі, що вони не вимагають цього.

Це дало невірні відповіді, поки я не передавав значення у Radians - тепер це майже те саме, що значення, отримані в додатку Apple Map Map :-)

Додаткове оновлення:

Якщо ви використовуєте iOS4 або новішу версію, то Apple надає деякі методи для цього, щоб досягти такої ж функціональності:

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

    MKMapPoint  start, finish;


    start = MKMapPointForCoordinate(place1);
    finish = MKMapPointForCoordinate(place2);

    return MKMetersBetweenMapPoints(start, finish) / 1000;
}

1
SDK iOS має власну реалізацію: developer.apple.com/library/ios/documentation/CoreLocation/… :
tuler

Я думаю, що дужки навколо pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))неправильні. Видаліть їх, і результат відповідає тому, що я отримую, коли використовую інші реалізації на цій сторінці або впроваджую формулу Haversine з Вікіпедії з нуля.
zanedp

Використовуючи координати (40.7127837, -74.0059413) для NYC та (34.052234, -118.243685) для LA, ()приблизно з цією сумою я отримую 3869.75. Без них я отримую 3935,75, що в значній мірі виявляється в пошуку в Інтернеті.
zanedp

40

Це проста функція PHP, яка дасть дуже розумне наближення (під +/- 1% похибки).

<?php
function distance($lat1, $lon1, $lat2, $lon2) {

    $pi80 = M_PI / 180;
    $lat1 *= $pi80;
    $lon1 *= $pi80;
    $lat2 *= $pi80;
    $lon2 *= $pi80;

    $r = 6372.797; // mean radius of Earth in km
    $dlat = $lat2 - $lat1;
    $dlon = $lon2 - $lon1;
    $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2);
    $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
    $km = $r * $c;

    //echo '<br/>'.$km;
    return $km;
}
?>

Як було сказано раніше; земля НЕ сфера. Це як старий, старий бейсбол, з яким Марк Макгвайр вирішив попрактикуватися - він повний вм'ятин і ударів. Простіші обчислення (як цей) трактують це як сферу.

Різні методи можуть бути більш-менш точними залежно від того, де ви знаходитесь на цьому неправильному яйцеподібному рівні та наскільки далеко розташовані ваші точки (чим ближче вони, тим менша межа абсолютної похибки). Чим точніше ваше очікування, тим складніша математика.

Для отримання додаткової інформації: географічна відстань вікіпедії


4
Це прекрасно працює! Я щойно додав $ distance_miles = $ km * 0.621371; і це все, що мені потрібно для приблизної відстані в милях! Дякую Тоні.

31

Я розміщую тут свій робочий приклад.

Перерахуйте всі точки таблиці, що мають відстань між позначеною точкою (ми використовуємо випадкову точку - лат: 45.20327, довга: 23.7806) менше 50 КМ, із широтою та довготою, у MySQL (поля таблиці - координатні та координатні).

Перерахуйте всі, які мають РОЗМІСТЬ <50, у кілометрах (вважається радіусом Землі 6371 КМ):

SELECT denumire, (6371 * acos( cos( radians(45.20327) ) * cos( radians( coord_lat ) ) * cos( radians( 23.7806 ) - radians(coord_long) ) + sin( radians(45.20327) ) * sin( radians(coord_lat) ) )) AS distanta 
FROM obiective 
WHERE coord_lat<>'' 
    AND coord_long<>'' 
HAVING distanta<50 
ORDER BY distanta desc

Наведений вище приклад був протестований у MySQL 5.0.95 та 5.5.16 (Linux).


Я думаю, що хорошим підходом може бути попередня фільтрація результатів за допомогою апроксимації, тому важка формула застосовується лише в деяких випадках. Особливо корисні, якщо у вас інші умови. Я використовую це для початкового aprox: stackoverflow.com/questions/1253499/…
Пато

28

В іншому відповідає реалізація в відсутня.

Обчислення відстані між двома точками досить просто з distmфункцією від geosphereпакета:

distm(p1, p2, fun = distHaversine)

де:

p1 = longitude/latitude for point(s)
p2 = longitude/latitude for point(s)
# type of distance calculation
fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid 

Оскільки земля не ідеально сферична, формула Вінсенті для еліпсоїдів , мабуть, найкращий спосіб обчислення відстаней. Таким чином у geosphereпакеті, який ви використовуєте тоді:

distm(p1, p2, fun = distVincentyEllipsoid)

Звичайно, вам не обов’язково використовувати geosphereпакет, ви також можете обчислити відстань в базі Rза допомогою функції:

hav.dist <- function(long1, lat1, long2, lat2) {
  R <- 6371
  diff.long <- (long2 - long1)
  diff.lat <- (lat2 - lat1)
  a <- sin(diff.lat/2)^2 + cos(lat1) * cos(lat2) * sin(diff.long/2)^2
  b <- 2 * asin(pmin(1, sqrt(a))) 
  d = R * b
  return(d)
}

Щоб переконатися, що я зрозумів, що ви сказали: Код, який ви даєте наприкінці посади: Це реалізація формули Вінсенті? Наскільки ви знаєте, це повинно дати таку ж відповідь, як дзвонити Вінсенті в геосферу? [У мене немає геосфери чи іншої бібліотеки; просто шукаю якийсь код, який потрібно включити в додаток для різних платформ. Я, звичайно, перевіряю деякі тестові випадки проти відомого хорошого калькулятора.]
ToolmakerSteve

1
@ToolmakerSteve функцією наприкінці моєї відповіді є реалізація методу Хаверсіна
Яап

Привіт @Jaap, я можу запитати, що таке одиниця вимірювання для формули? Це в метрах?
Джексон

11

Гаверсин, безумовно, є хорошою формулою для, мабуть, більшості випадків, інші відповіді вже включають його, тому я не збираюся займати місце. Але важливо зазначити, що незалежно від того, яка формула використовується (так, не лише одна). Через величезний діапазон точності, а також необхідний час обчислення. Вибір формули вимагає трохи більше продуманості, ніж проста відповідь, що не стосується мозку.

Ця публікація від людини в nasa - найкраща, яку я знайшов, обговорюючи варіанти

http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html

Наприклад, якщо ви просто сортуєте рядки за відстанню в радіусі 100 миль. Формула з плоским землею буде набагато швидшою, ніж гаверсин.

HalfPi = 1.5707963;
R = 3956; /* the radius gives you the measurement unit*/

a = HalfPi - latoriginrad;
b = HalfPi - latdestrad;
u = a * a + b * b;
v = - 2 * a * b * cos(longdestrad - longoriginrad);
c = sqrt(abs(u + v));
return R * c;

Зауважте, що існує лише один косинус і один квадратний корінь. Vs 9 з них за формулою Гаверсіна.


Це приємна можливість. Тільки майте на увазі, що рекомендована максимальна відстань у дискусії становить 12 миль, а не 100 , і навіть при цьому помилки можуть повзати до 30 метрів (100 футів), залежно від положення земної кулі.
Ерік Ву

7

Ви можете використовувати збірку в CLLocationDistance для обчислення цього:

CLLocation *location1 = [[CLLocation alloc] initWithLatitude:latitude1 longitude:longitude1];
CLLocation *location2 = [[CLLocation alloc] initWithLatitude:latitude2 longitude:longitude2];
[self distanceInMetersFromLocation:location1 toLocation:location2]

- (int)distanceInMetersFromLocation:(CLLocation*)location1 toLocation:(CLLocation*)location2 {
    CLLocationDistance distanceInMeters = [location1 distanceFromLocation:location2];
    return distanceInMeters;
}

У вашому випадку, якщо ви хочете кілометри, просто розділіть на 1000.


7

Мені не подобається додавати ще одну відповідь, але API Google Maps v.3 має сферичну геометрію (і більше). Перетворивши свій WGS84 в десятковий градус, ви можете зробити це:

<script src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry" type="text/javascript"></script>  

distance = google.maps.geometry.spherical.computeDistanceBetween(
    new google.maps.LatLng(fromLat, fromLng), 
    new google.maps.LatLng(toLat, toLng));

Жодного слова про те, наскільки точними є обчислення Google або навіть яка модель використовується (хоча вона каже "сферична", а не "геоїдна". До речі, відстань "прямої лінії", очевидно, буде відрізнятися від відстані, якщо їхати на поверхня землі, яка, як видається, всі припускають.


відстань у метрах. в якості альтернативи можна використовувати computeLength ()
electrobabe

7

Python implimentation Origin - центр суміжних Сполучених Штатів.

from haversine import haversine
origin = (39.50, 98.35)
paris = (48.8567, 2.3508)
haversine(origin, paris, miles=True)

Щоб отримати відповідь у кілометрах, просто встановіть милі = false.


1
Ви імпортуєте нестандартний пакет, який виконує всю роботу. Я не знаю, чи все це корисно.
Teepeemm

Пакет знаходиться в PyPI, Python Package Index, як пакет python 3 разом із numpy та scikit-learn. Не впевнений, чому один призначений для пакетів. Вони, як правило, досить корисні. Як відкритий код, можна також вивчити методи, що містяться. Я думаю, що багато хто вважає цей пакет корисним, тому я покину посаду, незважаючи на скорочення. Ура. :)
invoketheshell

7

Тут може бути більш просте рішення і більш правильне: периметр землі - 40 000 км на екваторі, приблизно 37 000 за кругозором Грінвіча (або будь-якої довготи). Таким чином:

pythagoras = function (lat1, lon1, lat2, lon2) {
   function sqr(x) {return x * x;}
   function cosDeg(x) {return Math.cos(x * Math.PI / 180.0);}

   var earthCyclePerimeter = 40000000.0 * cosDeg((lat1 + lat2) / 2.0);
   var dx = (lon1 - lon2) * earthCyclePerimeter / 360.0;
   var dy = 37000000.0 * (lat1 - lat2) / 360.0;

   return Math.sqrt(sqr(dx) + sqr(dy));
};

Я погоджуюся, що це слід точно налаштувати, оскільки я сам сказав, що це еліпсоїд, тому радіус, помножений на косинус, змінюється. Але це трохи точніше. Порівняно з Google Maps, це значно зменшило помилку.


Чи відповідає відстань цієї функції в км?
Вікі

Це так, тільки тому, що екватор і цикли довготи знаходяться в км. За кілометри просто розділіть 40000 і 37000 на 1.6. Відчуваючи себе чудовисько, ви можете перетворити його на Ris, помноживши приблизно на 7 або в парасанг, розділившись на 2,2 ;-)
Meymann

Це, здається, найкраща відповідь, запропонована тут. Я хотів би використовувати його, але мені просто цікаво, чи є спосіб перевірити правильність цього алгоритму. Я перевірив f (50,5,58,3). Він дає 832 км, тоді як за допомогою рухомої матриці.co.uk/scripts/latlong.html за допомогою формули "хаверсин" дається 899 км. Чи є така велика різниця?
Chong Lip Phang

Більше того, я думаю, що значення, повернене вищевказаним кодом, знаходиться в м, а не в км.
Chong Lip Phang

@ChongLipPhang - ПОПЕРЕДЖЕННЯ: Теорема Піфагора є розумним наближенням лише для малих площ , оскільки ця теорема передбачає, що земля є плоскою. Як крайній випадок, почніть на екваторі і рухайтеся на 90 градусів на схід і на 90 градусів на північ. Кінцевим результатом звичайно є північний полюс і такий же, як переміщення 0 градусів на схід і 90 градусів на північ; тому виконання sqrt (sqr (dx) + sqr (dy)) у першому випадку буде дико відключено. ~ sqrt (10km sqr + 10km sqr) ~ = 14,4 км проти правильної відстані ~ 10 км.
ToolmakerSteve

7

Усі наведені вище відповіді припускають, що земля - ​​це сфера. Однак більш точним наближенням було б облачне сфероїд.

a= 6378.137#equitorial radius in km
b= 6356.752#polar radius in km

def Distance(lat1, lons1, lat2, lons2):
    lat1=math.radians(lat1)
    lons1=math.radians(lons1)
    R1=(((((a**2)*math.cos(lat1))**2)+(((b**2)*math.sin(lat1))**2))/((a*math.cos(lat1))**2+(b*math.sin(lat1))**2))**0.5 #radius of earth at lat1
    x1=R*math.cos(lat1)*math.cos(lons1)
    y1=R*math.cos(lat1)*math.sin(lons1)
    z1=R*math.sin(lat1)

    lat2=math.radians(lat2)
    lons2=math.radians(lons2)
    R1=(((((a**2)*math.cos(lat2))**2)+(((b**2)*math.sin(lat2))**2))/((a*math.cos(lat2))**2+(b*math.sin(lat2))**2))**0.5 #radius of earth at lat2
    x2=R*math.cos(lat2)*math.cos(lons2)
    y2=R*math.cos(lat2)*math.sin(lons2)
    z2=R*math.sin(lat2)

    return ((x1-x2)**2+(y1-y2)**2+(z1-z2)**2)**0.5

6

Ось реалізація SQL для обчислення відстані в км,

SELECT UserId, ( 3959 * acos( cos( radians( your latitude here ) ) * cos( radians(latitude) ) * 
cos( radians(longitude) - radians( your longitude here ) ) + sin( radians( your latitude here ) ) * 
sin( radians(latitude) ) ) ) AS distance FROM user HAVING
distance < 5  ORDER BY distance LIMIT 0 , 5;

Щоб отримати детальнішу інформацію про реалізацію програмного моменту, ви можете просто пройти скрипт, наведений тут


5

Ось машинопис реалізація Haversine формули

static getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number): number {
    var deg2Rad = deg => {
        return deg * Math.PI / 180;
    }

    var r = 6371; // Radius of the earth in km
    var dLat = deg2Rad(lat2 - lat1);   
    var dLon = deg2Rad(lon2 - lon1);
    var a =
        Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(deg2Rad(lat1)) * Math.cos(deg2Rad(lat2)) *
        Math.sin(dLon / 2) * Math.sin(dLon / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    var d = r * c; // Distance in km
    return d;
}

5

Як вказувалося, точний розрахунок повинен враховувати, що земля не є досконалою сферою. Ось декілька порівнянь різних алгоритмів, запропонованих тут:

geoDistance(50,5,58,3)
Haversine: 899 km
Maymenn: 833 km
Keerthana: 897 km
google.maps.geometry.spherical.computeDistanceBetween(): 900 km

geoDistance(50,5,-58,-3)
Haversine: 12030 km
Maymenn: 11135 km
Keerthana: 10310 km
google.maps.geometry.spherical.computeDistanceBetween(): 12044 km

geoDistance(.05,.005,.058,.003)
Haversine: 0.9169 km
Maymenn: 0.851723 km
Keerthana: 0.917964 km
google.maps.geometry.spherical.computeDistanceBetween(): 0.917964 km

geoDistance(.05,80,.058,80.3)
Haversine: 33.37 km
Maymenn: 33.34 km
Keerthana: 33.40767 km
google.maps.geometry.spherical.computeDistanceBetween(): 33.40770 km

На невеликих відстанях алгоритм Кіертана схоже з алгоритмом Карт Google. Google Maps, схоже, не дотримується жодного простого алгоритму, припускаючи, що це може бути найбільш точним методом.

У будь-якому випадку, ось реалізація алгоритму Keerthana Javascript:

function geoDistance(lat1, lng1, lat2, lng2){
    const a = 6378.137; // equitorial radius in km
    const b = 6356.752; // polar radius in km

    var sq = x => (x*x);
    var sqr = x => Math.sqrt(x);
    var cos = x => Math.cos(x);
    var sin = x => Math.sin(x);
    var radius = lat => sqr((sq(a*a*cos(lat))+sq(b*b*sin(lat)))/(sq(a*cos(lat))+sq(b*sin(lat))));

    lat1 = lat1 * Math.PI / 180;
    lng1 = lng1 * Math.PI / 180;
    lat2 = lat2 * Math.PI / 180;
    lng2 = lng2 * Math.PI / 180;

    var R1 = radius(lat1);
    var x1 = R1*cos(lat1)*cos(lng1);
    var y1 = R1*cos(lat1)*sin(lng1);
    var z1 = R1*sin(lat1);

    var R2 = radius(lat2);
    var x2 = R2*cos(lat2)*cos(lng2);
    var y2 = R2*cos(lat2)*sin(lng2);
    var z2 = R2*sin(lat2);

    return sqr(sq(x1-x2)+sq(y1-y2)+sq(z1-z2));
}

4

Цей сценарій [в PHP] обчислює відстані між двома точками.

public static function getDistanceOfTwoPoints($source, $dest, $unit='K') {
        $lat1 = $source[0];
        $lon1 = $source[1];
        $lat2 = $dest[0];
        $lon2 = $dest[1];

        $theta = $lon1 - $lon2;
        $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
        $dist = acos($dist);
        $dist = rad2deg($dist);
        $miles = $dist * 60 * 1.1515;
        $unit = strtoupper($unit);

        if ($unit == "K") {
            return ($miles * 1.609344);
        }
        else if ($unit == "M")
        {
            return ($miles * 1.609344 * 1000);
        }
        else if ($unit == "N") {
            return ($miles * 0.8684);
        } 
        else {
            return $miles;
        }
    }

4

Реалізація Java у формулі Хаверсіна

double calculateDistance(double latPoint1, double lngPoint1, 
                         double latPoint2, double lngPoint2) {
    if(latPoint1 == latPoint2 && lngPoint1 == lngPoint2) {
        return 0d;
    }

    final double EARTH_RADIUS = 6371.0; //km value;

    //converting to radians
    latPoint1 = Math.toRadians(latPoint1);
    lngPoint1 = Math.toRadians(lngPoint1);
    latPoint2 = Math.toRadians(latPoint2);
    lngPoint2 = Math.toRadians(lngPoint2);

    double distance = Math.pow(Math.sin((latPoint2 - latPoint1) / 2.0), 2) 
            + Math.cos(latPoint1) * Math.cos(latPoint2)
            * Math.pow(Math.sin((lngPoint2 - lngPoint1) / 2.0), 2);
    distance = 2.0 * EARTH_RADIUS * Math.asin(Math.sqrt(distance));

    return distance; //km value
}

3

Щоб обчислити відстань між двома точками на кулі, потрібно зробити обчислення Великого кола .

Існує декілька бібліотек C / C ++, які допомагають з проекцією карт на MapTools, якщо вам потрібно повторно відрегулювати відстані на рівну поверхню. Для цього вам знадобиться проекційний рядок різних систем координат.

Ви також можете знайти MapWindow корисним інструментом для візуалізації точок. Також у якості відкритого джерела є корисним посібником щодо використання бібліотеки proj.dll, яка, як видається, є основною бібліотекою проекцій з відкритим кодом.


3

Ось прийнята реалізація відповіді, перенесена на Java на випадок, коли хтось цього потребує.

package com.project529.garage.util;


/**
 * Mean radius.
 */
private static double EARTH_RADIUS = 6371;

/**
 * Returns the distance between two sets of latitudes and longitudes in meters.
 * <p/>
 * Based from the following JavaScript SO answer:
 * http://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula,
 * which is based on https://en.wikipedia.org/wiki/Haversine_formula (error rate: ~0.55%).
 */
public double getDistanceBetween(double lat1, double lon1, double lat2, double lon2) {
    double dLat = toRadians(lat2 - lat1);
    double dLon = toRadians(lon2 - lon1);

    double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
                    Math.sin(dLon / 2) * Math.sin(dLon / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double d = EARTH_RADIUS * c;

    return d;
}

public double toRadians(double degrees) {
    return degrees * (Math.PI / 180);
}

2

Ось реалізація VB.NET, ця реалізація дасть результат у KM або милях на основі значення Enum, яке ви передаєте.

Public Enum DistanceType
    Miles
    KiloMeters
End Enum

Public Structure Position
    Public Latitude As Double
    Public Longitude As Double
End Structure

Public Class Haversine

    Public Function Distance(Pos1 As Position,
                             Pos2 As Position,
                             DistType As DistanceType) As Double

        Dim R As Double = If((DistType = DistanceType.Miles), 3960, 6371)

        Dim dLat As Double = Me.toRadian(Pos2.Latitude - Pos1.Latitude)

        Dim dLon As Double = Me.toRadian(Pos2.Longitude - Pos1.Longitude)

        Dim a As Double = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(Me.toRadian(Pos1.Latitude)) * Math.Cos(Me.toRadian(Pos2.Latitude)) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2)

        Dim c As Double = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a)))

        Dim result As Double = R * c

        Return result

    End Function

    Private Function toRadian(val As Double) As Double

        Return (Math.PI / 180) * val

    End Function

End Class

Обчисливши "а", ви два рази помилково писали Math.Sin ( dLat ..)?
Марко Оттіна

2

Я скоротив обчислення, спростивши формулу.

Ось вона в Рубі:

include Math
earth_radius_mi = 3959
radians = lambda { |deg| deg * PI / 180 }
coord_radians = lambda { |c| { :lat => radians[c[:lat]], :lng => radians[c[:lng]] } }

# from/to = { :lat => (latitude_in_degrees), :lng => (longitude_in_degrees) }
def haversine_distance(from, to)
  from, to = coord_radians[from], coord_radians[to]
  cosines_product = cos(to[:lat]) * cos(from[:lat]) * cos(from[:lng] - to[:lng])
  sines_product = sin(to[:lat]) * sin(from[:lat])
  return earth_radius_mi * acos(cosines_product + sines_product)
end

2
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2,units) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; 
  var miles = d / 1.609344; 

if ( units == 'km' ) {  
return d; 
 } else {
return miles;
}}

Рішення Чак, дійсне і для миль.


2

Ось моя реалізація Java для розрахунку відстані через десяткові градуси після деякого пошуку. Я використовував середній радіус світу (з вікіпедії) в км. Якщо ви хочете результат милі, то використовуйте радіус світу в милях.

public static double distanceLatLong2(double lat1, double lng1, double lat2, double lng2) 
{
  double earthRadius = 6371.0d; // KM: use mile here if you want mile result

  double dLat = toRadian(lat2 - lat1);
  double dLng = toRadian(lng2 - lng1);

  double a = Math.pow(Math.sin(dLat/2), 2)  + 
          Math.cos(toRadian(lat1)) * Math.cos(toRadian(lat2)) * 
          Math.pow(Math.sin(dLng/2), 2);

  double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

  return earthRadius * c; // returns result kilometers
}

public static double toRadian(double degrees) 
{
  return (degrees * Math.PI) / 180.0d;
}

2

У Mysql використовуйте наступну функцію передавання параметрів як використовувану POINT(LONG,LAT)

CREATE FUNCTION `distance`(a POINT, b POINT)
 RETURNS double
    DETERMINISTIC
BEGIN

RETURN

GLength( LineString(( PointFromWKB(a)), (PointFromWKB(b)))) * 100000; -- To Make the distance in meters

END;

2
function getDistanceFromLatLonInKm(position1, position2) {
    "use strict";
    var deg2rad = function (deg) { return deg * (Math.PI / 180); },
        R = 6371,
        dLat = deg2rad(position2.lat - position1.lat),
        dLng = deg2rad(position2.lng - position1.lng),
        a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
            + Math.cos(deg2rad(position1.lat))
            * Math.cos(deg2rad(position1.lat))
            * Math.sin(dLng / 2) * Math.sin(dLng / 2),
        c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c;
}

console.log(getDistanceFromLatLonInKm(
    {lat: 48.7931459, lng: 1.9483572},
    {lat: 48.827167, lng: 2.2459745}
));

2

ось приклад у postgres sql (у км, за версію за милі, замініть 1.609344 на версію 0.8684)

CREATE OR REPLACE FUNCTION public.geodistance(alat float, alng float, blat  

float, blng  float)
  RETURNS float AS
$BODY$
DECLARE
    v_distance float;
BEGIN

    v_distance = asin( sqrt(
            sin(radians(blat-alat)/2)^2 
                + (
                    (sin(radians(blng-alng)/2)^2) *
                    cos(radians(alat)) *
                    cos(radians(blat))
                )
          )
        ) * cast('7926.3352' as float) * cast('1.609344' as float) ;


    RETURN v_distance;
END 
$BODY$
language plpgsql VOLATILE SECURITY DEFINER;
alter function geodistance(alat float, alng float, blat float, blng float)
owner to postgres;

2

Ось ще один перетворений у Ruby код:

include Math
#Note: from/to = [lat, long]

def get_distance_in_km(from, to)
  radians = lambda { |deg| deg * Math.PI / 180 }
  radius = 6371 # Radius of the earth in kilometer
  dLat = radians[to[0]-from[0]]
  dLon = radians[to[1]-from[1]]

  cosines_product = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(radians[from[0]]) * Math.cos(radians[to[1]]) * Math.sin(dLon/2) * Math.sin(dLon/2)

  c = 2 * Math.atan2(Math.sqrt(cosines_product), Math.sqrt(1-cosines_product)) 
  return radius * c # Distance in kilometer
end

1

тут є хороший приклад для розрахунку відстані за допомогою PHP http://www.geodatasource.com/developers/php :

 function distance($lat1, $lon1, $lat2, $lon2, $unit) {

     $theta = $lon1 - $lon2;
     $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
     $dist = acos($dist);
     $dist = rad2deg($dist);
     $miles = $dist * 60 * 1.1515;
     $unit = strtoupper($unit);

     if ($unit == "K") {
         return ($miles * 1.609344);
     } else if ($unit == "N") {
          return ($miles * 0.8684);
     } else {
          return $miles;
     }
 }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.