위도 / 경도를 기준으로 두 점 사이의 거리 얻기


157

이 공식을 구현하려고했습니다 : 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 입니다. 왜?

답변:


206

편집 : 참고로 두 점 사이의 거리를 빠르고 쉽게 찾는 방법이 필요하면 Haversine을 다시 구현하는 대신 아래 Kurt의 답변에 설명 된 접근 방식을 사용하는 것이 좋습니다 .

이 답변은 OP가 발생한 특정 버그에 응답하는 데 중점을 둡니다.


파이썬에서는 모든 삼각 함수가 각도 가 아닌 라디안을 사용 하기 때문입니다 .

숫자를 수동으로 라디안으로 변환하거나 radians수학 모듈 의 함수를 사용할 수 있습니다.

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.545589351km 값을 반환합니다 .


13
이것은 모든 프로그래밍 언어와 미분 미적분학에서도 마찬가지입니다. 학위를 사용하는 것은 예외이며 사람의 말에만 사용됩니다.
bluesmoon

11
지혜롭게도이 공식은 모든 각도가 양수 여야합니다. radians(abs(52.123))트릭을해야합니다 ...
Richard Dunn

1
모든 각도 (각도)가 양수인지 확실합니까? 나는 이것이 틀렸다고 생각한다. lat1, lon1 = 10, 10 (도) 및 lat2, lon2 = -10, -10 (도)인지 고려하십시오. 도 주위에 abs ()를 추가하면 거리가 0이되고 이는 올바르지 않습니다. 아마도 당신은 dlon 및 / 또는 dlat의 절대 값을 취하려고했지만, dlon의 계산에서 dlat 값을 보면 사인은 짝수 함수이며 코사인 제곱은 짝수 함수이므로 dlat 또는 dlon의 절대 값을 얻는 것의 이점을 참조하십시오.
Dave LeCompte

238

업데이트 : 04/2018 : GeoPy 버전 1.13 이후 Vincenty 거리는 더 이상 사용되지 않습니다 . 대신 geopy.distance.distance ()를 사용해야합니다!


위의 답변 은 지구가 구체라고 가정 하는 Haversine 공식을 기반으로하며 , 이로 인해 최대 약 0.5 %의 오차가 발생합니다 help(geopy.distance). Vincenty 거리는 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또는 다른 여러 거리 단위 중 하나를 선택할 수도 있습니다 ).


1
감사. 뉴 포트와 클리블랜드 대신 내가 제공 한 좌표로 답변을 업데이트 할 수 있습니까? 미래 독자들에게 더 나은 이해를 줄 것입니다.
gwaramadze

1
: 뉴 포트 클리블랜드의 임의의 위치가 나열 PyPI의 예 geopy 문서에서 온 pypi.python.org/pypi/geopy
제이슨 파햄

나는 이것에 대한 Kurt Peek의 답변을 수정해야했다 : 자본화 필요 :print geopy.distance.VincentyDistance(coords_1, coords_2).km 279.352901604
Jim

4
geopy.distance.distance(…)현재 최고 (= 가장 정확한) 거리 공식의 별칭 인 코드에서 사용해야합니다 . (현재의
세기

10
geopy-1.18.1 출력에서 ​​geopy.distance.vincenty 사용 : Vincenty는 더 이상 사용되지 않으며 geopy 2.0에서 제거 될 예정입니다. 더 정확하고 항상 수렴하는 대신 geopy.distance.geodesic(또는 기본값 geopy.distance.distance)을 대신 사용하십시오 .
juanmah

88

검색 엔진을 통해 여기에 와서 즉시 사용할 수있는 솔루션을 찾는 사람들 (나 같은 사람)에게는을 설치하는 것이 좋습니다 mpu. 그것을 통해 설치하고 pip install mpu --user다음과 같이 사용하여 haversine 거리 를 얻으십시오 :

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][1]

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])

포인트 중 하나의 주어진 Highet을 변경하는 방법이 있습니까?
yovel cohen

높이 차이를 단순히 거리에 추가 할 수 있습니다. 그래도 그렇게하지 않을 것입니다.
마틴 토마

16

어쨌든 프로젝트 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

지오피


5
import numpy as np


def Haversine(lat1,lon1,lat2,lon2, **kwarg):
    """
    This uses the ‘haversine’ formula to calculate the great-circle distance between two points – that is, 
    the shortest distance over the earth’s surface – giving an ‘as-the-crow-flies’ distance between the points 
    (ignoring any hills they fly over, of course!).
    Haversine
    formula:    a = sin²(Δφ/2) + cos φ1 ⋅ cos φ2 ⋅ sin²(Δλ/2)
    c = 2 ⋅ atan2( √a, √(1−a) )
    d = R ⋅ c
    where   φ is latitude, λ is longitude, R is earth’s radius (mean radius = 6,371km);
    note that angles need to be in radians to pass to trig functions!
    """
    R = 6371.0088
    lat1,lon1,lat2,lon2 = map(np.radians, [lat1,lon1,lat2,lon2])

    dlat = lat2 - lat1
    dlon = lon2 - lon1
    a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2) **2
    c = 2 * np.arctan2(a**0.5, (1-a)**0.5)
    d = R * c
    return round(d,4)

0

좌표를 기반으로 거리, 즉 위도와 경도를 계산하는 여러 가지 방법이 있습니다

설치 및 가져 오기

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]

Haversine 사용

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과 함께 haversine 사용

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.