Рассчитать расстояние между двумя точками широты и долготы? (Формула Haversine)

908

Как рассчитать расстояние между двумя точками, указанными по широте и долготе?

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

Робин Минто
источник
Для большей точности - см. Stackoverflow.com/questions/1420045/…
Лиор Коган
3
Обратите внимание, что вы не можете применить формулу Хаверсайна к эллипсоиду вращения, подобному WGS 84. Этот метод можно применять только к сфере с радиусом.
Майк Т
3
Большинство ответов здесь используют простую сферическую тригонометрию, поэтому результаты довольно приблизительны по сравнению с эллипсоидальными расстояниями WGS84, используемыми в системе GPS. Некоторые из ответов относятся к формуле Винсенти для эллипсоидов, но этот алгоритм был разработан для использования на настольных калькуляторах эпохи 1960-х годов и имеет проблемы со стабильностью и точностью; сейчас у нас лучше аппаратное и программное обеспечение. Пожалуйста, смотрите GeographicLib для высококачественной библиотеки с реализациями на разных языках.
PM 2Ring
@MikeT - правда, хотя многие ответы здесь кажутся полезными на небольших расстояниях : если вы берете широту / долготу из WGS 84 и применяете Haversine, как если бы это были точки на сфере, разве вы не получите ответы, ошибки которых связаны только с фактор выравнивания Земли, так что, возможно, в пределах 1% от более точной формулы? С оговоркой, что это небольшие расстояния, скажем, в пределах одного города.
ToolmakerSteve
1
Для этих платформ: Mono / .NET 4.5 / .NET Core / Windows Phone 8.x / Универсальная платформа Windows / Xamarin iOS / Xamarin Android см. Stackoverflow.com/a/54296314/2736742
А. Морель

Ответы:

1149

Эта ссылка может быть полезной для вас, так как она детализирует использование формулы Haversine для расчета расстояния.

Выдержка:

Этот скрипт [в Javascript] вычисляет расстояния между двумя точками, то есть кратчайшее расстояние над поверхностью земли, используя формулу «Haversine».

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))²
Жан Хоминал
360

Мне нужно было рассчитать много расстояний между точками для моего проекта, поэтому я пошел дальше и попытался оптимизировать код, который я нашел здесь. В среднем в разных браузерах моя новая реализация работает в 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...

И ради полноты: Haversine на вики.

Сальвадор Дали
источник
13
@AngularM, и, скорее всего, Google вычислит расстояние, если вы будете двигаться по некоторым дорогам, а не по прямой.
Сальвадор Дали
3
Google вычисляет расстояние вождения, это вычисляет "как летит ворона"
Hobbyist
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;
    }

}
jaircazarin-старый счет
источник
14
Вы используете экваториальный радиус, но вы должны использовать средний радиус, который составляет 6371 км
Филипп Лейберт
7
Не должно ли это быть double dlon = Radians(lon2 - lon1);иdouble dlat = Radians(lat2 - lat1);
Крис Марисик
Я согласен с Крисом Марисиком. Я использовал оригинальный код и расчеты были неверными. Я добавил вызов для преобразования дельт в радианы, и теперь он работает правильно. Я отправил изменения и жду, пока они не будут рецензированы.
Брайан Бедард
Я отправил еще одно редактирование, потому что lat1 и lat2 также должны быть преобразованы в радианы. Я также пересмотрел формулу для присвоения a, чтобы соответствовать формуле и коду, найденному здесь: movable-type.co.uk/scripts/latlong.html
Брайан Бедард
делает RADIUSзначение необходимости быть 6371 , как и в других ответах?
Крис Хейс
66

Вот Java-реализация формулы Haversine.

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

Обратите внимание, что здесь мы округляем ответ до ближайшего километра.

whostolebenfrog
источник
2
Если бы мы хотели рассчитать расстояние между двумя точками в метрах, что было бы более точным способом? Использовать 6371000в качестве радиуса земли? (средний радиус земли составляет 6371000 метров) или вы переводите километры в метры из вашей функции?
Micro
если вы хотите мили, 0.621371
умножьте
42

Большое спасибо за все это. Я использовал следующий код в своем приложении Objective-C для iPhone:

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 (), поскольку расстояния, которые я использую, настолько малы, что им это не требуется.

Он давал неправильные ответы, пока я не передал значения в радианах - теперь они в значительной степени совпадают со значениями, полученными из приложения Apple 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
iOS SDK имеет собственную реализацию: developer.apple.com/library/ios/documentation/CoreLocation/… :
tuler
Я думаю, что круглые скобки pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))неверны. Удалите их, и результат совпадет с тем, что я получу, когда использую другие реализации на этой странице или реализую формулу Хаверсайна из Википедии с нуля.
Zanedp
Используя координаты (40.7127837, -74.0059413) для Нью-Йорка и (34.052234, -118.243685) для Лос-Анджелеса, с ()этой суммой я получаю 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 (поля таблицы :ordin_lat иordin_long):

Укажите все ДИСТАНЦИЯ <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).

conualfy
источник
Я думаю, что хорошим подходом может быть предварительная фильтрация результатов с использованием аппроксимации, поэтому тяжелая формула применяется только в некоторых случаях. Особенно полезно, если у вас есть другие условия. Я использую это для первоначального 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
Привет @Jaap Могу я спросить, что является единицей измерения для формулы? Это в метрах?
Джексон
11

Гаверсин, безусловно, является хорошей формулой для большинства случаев, другие ответы уже включают ее, поэтому я не собираюсь занимать место. Но важно отметить, что независимо от того, какая формула используется (да, не только одна). Из-за огромного диапазона возможной точности, а также требуемого времени вычислений. Выбор формулы требует немного больше обдумывания, чем простой ответ.

Это сообщение от человека из НАСА, лучшее, что я нашел при обсуждении вариантов

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

Например, если вы просто сортируете строки по расстоянию в радиусе 100 миль. Формула плоской земли будет намного быстрее, чем haversine.

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;

Обратите внимание, что есть только один косинус и один квадратный корень. Против 9 из них по формуле Haversine.

Артуро Эрнандес
источник
Это хорошая возможность. Просто имейте в виду, что рекомендуемое максимальное расстояние в обсуждении составляет 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 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 Origin - центр смежных Соединенных Штатов.

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

Чтобы получить ответ в километрах, просто установите мили = false.

invoketheshell
источник
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 и это значительно уменьшило ошибку.

Meymann
источник
Эта функция возвращает расстояние в км?
Викки
Это просто потому, что экватора и долготы циклы в км. Для миль просто разделите 40000 и 37000 на 1,6. Чувствуя себя отвратительным, вы можете преобразовать его в Ris, умноженное примерно на 7 или на парасанг, разделив на 2,2 ;-)
Meymann
Это, кажется, лучший ответ, предлагаемый здесь. Я хотел бы использовать его, но мне просто интересно, есть ли способ проверить правильность этого алгоритма. Я проверил F (50,5,58,3). Он дает 832 км, тогда как movable-type.co.uk/scripts/latlong.html с использованием формулы «haversine» дает 899 км. Есть ли такая большая разница?
Чонг Лип Пханг
Более того, я думаю, что значение, возвращаемое приведенным выше кодом, указывается в м, а не в км.
Чонг Лип Пханг
@ChongLipPhang - ВНИМАНИЕ: теорема Пифагора является лишь разумным приближением для небольших областей , так как эта теорема предполагает, что земля плоская. В крайнем случае, начните с экватора и двигайтесь на 90 градусов на восток и на 90 градусов на север. Конечным результатом, конечно, является северный полюс, и он такой же, как и перемещение на 0 градусов на восток и на 90 градусов на север; так что выполнение sqrt (sqr (dx) + sqr (dy)) в первом случае будет дико отключено. ~ 10 км (10 км + 10 км) ~ = 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;

Для получения более подробной информации о реализации путем программирования языка, вы можете просто пройти через скрипт php, приведенный здесь.

Киран Мания
источник
5

Вот машинописная реализация формулы Хаверсайна

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;
}
Sel
источник
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

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

Во всяком случае, вот реализация Javascript алгоритма Keerthana:

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 в соответствии с формулой Haversine

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
}
ак-J
источник
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, эта реализация даст вам результат в КМ или Милях на основе переданного вами значения 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
Taiseer Joudeh
источник
Рассчитав «а», вы дважды по ошибке написали Math.Sin ( dLat ..)?
Марко Оттина
2

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

Вот это в Ruby:

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

Решение Чака, действительное на мили тоже.

MPaulo
источник
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;
shanavascet
источник
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
aldrien.h
источник
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;
     }
 }
ayalcinkaya
источник