Получение расстояния между двумя точками на основе широты / долготы

Я попытался реализовать эту формулу: http://andrew.hedges.name/experiments/haversine/ Аплет подходит для двух пунктов, которые я тестирую:

введите описание изображения здесь

Но мой код не работает.

from math import sin, cos, sqrt, atan2

R = 6373.0

lat1 = 52.2296756
lon1 = 21.0122287
lat2 = 52.406374
lon2 = 16.9251681

dlon = lon2 - lon1
dlat = lat2 - lat1
a = (sin(dlat/2))**2 + cos(lat1) * cos(lat2) * (sin(dlon/2))**2
c = 2 * atan2(sqrt(a), sqrt(1-a))
distance = R * c

print "Result", distance
print "Should be", 278.546

Возвращаемое им расстояние составляет 5447.05546147. Почему?


person gwaramadze    schedule 16.10.2013    source источник


Ответы (7)


Изменить: в качестве примечания: если вам просто нужен быстрый и простой способ определения расстояния между двумя точками, я настоятельно рекомендую использовать подход, описанный в ответ Курта ниже вместо повторной реализации Хаверсина - см. его сообщение для обоснования.

Этот ответ посвящен только ответу на конкретную ошибку, с которой столкнулся OP.


Это потому, что в Python все триггерные функции используют радианы, а не градусов.

Вы можете преобразовать числа в радианы вручную или использовать _1 _ функция из математического модуля:

from math import sin, cos, sqrt, atan2, radians

# approximate radius of earth in km
R = 6373.0

lat1 = radians(52.2296756)
lon1 = radians(21.0122287)
lat2 = radians(52.406374)
lon2 = radians(16.9251681)

dlon = lon2 - lon1
dlat = lat2 - lat1

a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1 - a))

distance = R * c

print("Result:", distance)
print("Should be:", 278.546, "km")

Расстояние теперь возвращает правильное значение 278.545589351 км.

person Michael0x2a    schedule 16.10.2013
comment
это верно для любого языка программирования, а также для дифференциального исчисления. использование степеней является исключением и используется только в человеческой речи. - person bluesmoon; 17.10.2013
comment
Слово мудрым, эта формула требует, чтобы все степени были положительными. radians(abs(52.123)) должен помочь ... - person Richard Dunn; 04.07.2017
comment
Вы уверены, что все градусы (углы?) Положительны? Я считаю это неправильным. Рассмотрим, если lat1, lon1 = 10, 10 (градусы) и lat2, lon2 = -10, -10 (градусы). Если добавить абс () вокруг градусов, расстояние будет равно нулю, что неверно. Возможно, вы хотели взять абсолютное значение dlon и / или dlat, но если вы посмотрите на значения dlon, dlat при вычислении a, синус является четной функцией, а косинус в квадрате - четной функцией, поэтому я не также вижу какую-либо пользу от использования абсолютного значения dlat или dlon. - person Dave LeCompte; 24.05.2020
comment
Просто интересно, является ли указанное выше расстояние расстоянием по дуге или расстоянием по плоскости между двумя точками? - person Punita Ojha; 07.07.2021

Обновление: 04/2018: Обратите внимание, что расстояние Винсенти устарело, начиная с версии GeoPy 1.13 - вместо этого используйте geopy.distance.distance ()!


Приведенные выше ответы основаны на формуле Хаверсина, которая предполагает, что Земля является сферой, что приводит к погрешности до 0,5% (по данным help(geopy.distance)). Расстояние Винсенти использует более точные эллипсоидальные модели, такие как WGS-84 и реализован в геопия. Например,

import geopy.distance

coords_1 = (52.2296756, 21.0122287)
coords_2 = (52.406374, 16.9251681)

print geopy.distance.vincenty(coords_1, coords_2).km

напечатает расстояние 279.352901604 километров, используя эллипсоид по умолчанию WGS-84. (Вы также можете выбрать .miles или одну из нескольких других единиц расстояния).

person Kurt Peek    schedule 04.04.2017
comment
Спасибо. Не могли бы вы обновить свой ответ, указав координаты, которые я указал вместо Ньюпорта и Кливленда. Это даст лучшее понимание будущим читателям. - person gwaramadze; 05.04.2017
comment
Произвольные местоположения Ньюпорта и Кливленда взяты из примера документации по географии в листинге PyPI: pypi.python.org/ pypi / geopy - person Jason Parham; 29.05.2017
comment
Мне пришлось изменить ответ Курта Пика на это: Требуется использование заглавных букв: print geopy.distance.VincentyDistance(coords_1, coords_2).km 279.352901604 - person Jim; 14.06.2017
comment
Вероятно, вам следует использовать geopy.distance.distance(…) в коде, который является псевдонимом лучшей на данный момент (= наиболее точной) формулы расстояния. (Винсенти в данный момент.) - person mbirth; 13.01.2018
comment
Вероятно, вам следует использовать geopy.distance.geodesic, поскольку расстояние вызовет ValueError, если c1=(1, 179) и c2=(0,0). - person ramwin; 04.07.2018
comment
Это дает расстояние или смещение? Я имею в виду, учитывает ли он какую-либо вертикальную ось? - person deadcode; 10.12.2018
comment
Использование geopy.distance.vincenty в выходных данных geopy-1.18.1: Vincenty устарел и будет удален в geopy 2.0. Вместо этого используйте geopy.distance.geodesic (или geopy.distance.distance по умолчанию), который более точен и всегда сходится. - person juanmah; 08.03.2019
comment
geopy.distance.great_circle будет работать в два раза быстрее - person nda; 22.02.2020
comment
DeprecationWarning: Vincenty устарел и будет удален в geopy 2.0. Вместо этого используйте geopy.distance.geodesic (или geopy.distance.distance по умолчанию), который более точен и всегда сходится. #FYI - person えるまる; 23.04.2020
comment
Обновите geopy 2.0.0: from geopy import distance, а затем используйте dist = distance.distance(coords_1, coords_2).km, который по умолчанию используется для получения геодезического расстояния. - person Colonel_Old; 23.09.2020

Людям (вроде меня), которые приходят сюда через поисковую систему и просто ищут решение, которое работает "из коробки", я рекомендую установить mpu. Установите его через pip install mpu --user и используйте его следующим образом, чтобы получить гаверсинусное расстояние:

import mpu

# Point one
lat1 = 52.2296756
lon1 = 21.0122287

# Point two
lat2 = 52.406374
lon2 = 16.9251681

# What you were looking for
dist = mpu.haversine_distance((lat1, lon1), (lat2, lon2))
print(dist)  # gives 278.45817507541943.

Альтернативный пакет - gpxpy.

Если вам не нужны зависимости, вы можете использовать:

import math


def distance(origin, destination):
    """
    Calculate the Haversine distance.

    Parameters
    ----------
    origin : tuple of float
        (lat, long)
    destination : tuple of float
        (lat, long)

    Returns
    -------
    distance_in_km : float

    Examples
    --------
    >>> origin = (48.1372, 11.5756)  # Munich
    >>> destination = (52.5186, 13.4083)  # Berlin
    >>> round(distance(origin, destination), 1)
    504.2
    """
    lat1, lon1 = origin
    lat2, lon2 = destination
    radius = 6371  # km

    dlat = math.radians(lat2 - lat1)
    dlon = math.radians(lon2 - lon1)
    a = (math.sin(dlat / 2) * math.sin(dlat / 2) +
         math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) *
         math.sin(dlon / 2) * math.sin(dlon / 2))
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    d = radius * c

    return d


if __name__ == '__main__':
    import doctest
    doctest.testmod()

Другой альтернативный пакет - haversine.

from haversine import haversine, Unit

lyon = (45.7597, 4.8422) # (lat, lon)
paris = (48.8567, 2.3508)

haversine(lyon, paris)
>> 392.2172595594006  # in kilometers

haversine(lyon, paris, unit=Unit.MILES)
>> 243.71201856934454  # in miles

# you can also use the string abbreviation for units:
haversine(lyon, paris, unit='mi')
>> 243.71201856934454  # in miles

haversine(lyon, paris, unit=Unit.NAUTICAL_MILES)
>> 211.78037755311516  # in nautical miles

Они утверждают, что оптимизировали производительность для расстояний между всеми точками в двух векторах.

from haversine import haversine_vector, Unit

lyon = (45.7597, 4.8422) # (lat, lon)
paris = (48.8567, 2.3508)
new_york = (40.7033962, -74.2351462)

haversine_vector([lyon, lyon], [paris, new_york], Unit.KILOMETERS)

>> array([ 392.21725956, 6163.43638211])
person Martin Thoma    schedule 04.07.2016
comment
Есть ли способ изменить данное значение одной из точек? - person ; 06.12.2019
comment
Вы можете просто добавить разницу в высоте к расстоянию. Но я бы не стал этого делать. - person Martin Thoma; 05.05.2020
comment
Лион, Париж, 392,2172595594006 км, последняя цифра даже не равна размеру атома водорода. Очень точный! - person mins; 26.10.2020

Я пришел к гораздо более простому и надежному решению, в котором используется пакет geodesic из geopy, поскольку вы, скорее всего, все равно будете использовать его в своем проекте, поэтому установка дополнительных пакетов не требуется.

Вот мое решение:

from geopy.distance import geodesic


origin = (30.172705, 31.526725)  # (latitude, longitude) don't confuse
dist = (30.288281, 31.732326)

print(geodesic(origin, dist).meters)  # 23576.805481751613
print(geodesic(origin, dist).kilometers)  # 23.576805481751613
print(geodesic(origin, dist).miles)  # 14.64994773134371

geopy

person Ramy M. Mousa    schedule 10.08.2019

Есть несколько способов рассчитать расстояние на основе координат, т. Е. Широты и долготы.

Установить и импортировать

from geopy import distance
from math import sin, cos, sqrt, atan2, radians
from sklearn.neighbors import DistanceMetric
import osrm
import numpy as np

Определить координаты

lat1, lon1, lat2, lon2, R = 20.9467,72.9520, 21.1702, 72.8311, 6373.0
coordinates_from = [lat1, lon1]
coordinates_to = [lat2, lon2]

Использование гаверсина

dlon = radians(lon2) - radians(lon1)
dlat = radians(lat2) - radians(lat1)
    
a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
    
distance_haversine_formula = R * c
print('distance using haversine formula: ', distance_haversine_formula)

Использование гаверсина со sklearn

dist = DistanceMetric.get_metric('haversine')
    
X = [[radians(lat1), radians(lon1)], [radians(lat2), radians(lon2)]]
distance_sklearn = R * dist.pairwise(X)
print('distance using sklearn: ', np.array(distance_sklearn).item(1))

Использование OSRM

osrm_client = osrm.Client(host='http://router.project-osrm.org')
coordinates_osrm = [[lon1, lat1], [lon2, lat2]] # note that order is lon, lat
    
osrm_response = osrm_client.route(coordinates=coordinates_osrm, overview=osrm.overview.full)
dist_osrm = osrm_response.get('routes')[0].get('distance')/1000 # in km
print('distance using OSRM: ', dist_osrm)

Использование геопии

distance_geopy = distance.distance(coordinates_from, coordinates_to).km
print('distance using geopy: ', distance_geopy)
    
distance_geopy_great_circle = distance.great_circle(coordinates_from, coordinates_to).km 
print('distance using geopy great circle: ', distance_geopy_great_circle)

Выход

distance using haversine formula:  26.07547017310917
distance using sklearn:  27.847882224769783
distance using OSRM:  33.091699999999996
distance using geopy:  27.7528030550408
distance using geopy great circle:  27.839182219511834
person Patel Romil    schedule 26.07.2020

Вы можете использовать функцию Uber H3, point_dist() для вычисления сферического расстояния между двумя точками (широта, долгота). . Мы можем установить единицу возврата (км, м или рад). Единица измерения по умолчанию - км.

Пример :

import H3

coords_1 = (52.2296756, 21.0122287)
coords_2 = (52.406374, 16.9251681)
distance = h3.point_dist(coords_1,coords_2) #278.4584889328128

Надеюсь, это будет полезно!

person Ransaka Ravihara    schedule 23.02.2021

person    schedule
comment
Привет, как вы думаете, есть ли способ выполнить расчет при получении данных непосредственно из шаблона? - person Louis; 03.11.2020