Khoảng cách giữa hai điểm dựa trên vĩ độ / kinh độ


156

Tôi đã thử thực hiện công thức này: http://andrew.hedges.name/experiment/haversine/ Ứng dụng này rất tốt cho hai điểm tôi đang thử nghiệm:

nhập mô tả hình ảnh ở đây

Tuy nhiên, mã của tôi không hoạt động.

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

Khoảng cách nó trả về là 5447.05546147 . Tại sao?

Câu trả lời:


206

Chỉnh sửa: Cũng giống như một ghi chú, nếu bạn chỉ cần một cách nhanh chóng và dễ dàng để tìm khoảng cách giữa hai điểm, tôi khuyên bạn nên sử dụng phương pháp được mô tả trong câu trả lời của Kurt bên dưới thay vì thực hiện lại Haversine - xem bài đăng của anh ấy để biết lý do.

Câu trả lời này chỉ tập trung vào việc trả lời lỗi cụ thể mà OP gặp phải.


Đó là bởi vì trong Python, tất cả các hàm lượng giác đều sử dụng radian chứ không phải độ.

Bạn có thể chuyển đổi số theo cách thủ công sang radian hoặc sử dụng radianshàm từ mô-đun toán học:

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

Khoảng cách hiện đang trả về giá trị chính xác của 278.545589351km.


13
điều này đúng trong bất kỳ ngôn ngữ lập trình nào và cả trong phép tính vi phân. sử dụng độ là ngoại lệ, và chỉ được sử dụng trong lời nói của con người.
bluesmoon

11
Từ để khôn ngoan, công thức này đòi hỏi tất cả các độ là tích cực. radians(abs(52.123))nên thực hiện mánh khóe ...
Richard Dunn

1
Bạn có chắc chắn về tất cả các độ (góc?) Là tích cực? Tôi nghĩ rằng điều này là sai. Xem xét nếu lat1, lon1 = 10, 10 (độ) và lat2, lon2 = -10, -10 (độ). Bằng cách thêm abs () quanh các độ, khoảng cách sẽ bằng 0, không chính xác. Có lẽ bạn muốn lấy giá trị tuyệt đối của dlon và / hoặc dlat, nhưng nếu bạn nhìn vào dlon, các giá trị dlat trong phép tính a, sin là một hàm chẵn và bình phương cosine là một hàm chẵn, vì vậy tôi không thấy bất kỳ lợi ích nào để lấy một giá trị tuyệt đối của dlat hoặc dlon.
Dave LeCompte

238

Cập nhật: 04/2018: Lưu ý rằng khoảng cách Vincenty không được chấp nhận kể từ phiên bản GeoPy 1.13 - bạn nên sử dụng geopy.distance.distance () thay thế!


Các câu trả lời ở trên dựa trên công thức Haversine , giả sử trái đất là một hình cầu, dẫn đến sai số lên tới khoảng 0,5% (theo help(geopy.distance)). Khoảng cách Vincenty sử dụng các mô hình elip chính xác hơn như WGS-84 và được triển khai trong địa lý . Ví dụ,

import geopy.distance

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

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

sẽ in khoảng cách 279.352901604km bằng cách sử dụng ellipsoid mặc định WGS-84. (Bạn cũng có thể chọn .mileshoặc một trong một số đơn vị khoảng cách khác).


1
Cảm ơn. Bạn có thể vui lòng cập nhật câu trả lời của bạn với tọa độ tôi cung cấp trong câu hỏi thay vì Newport và Cleveland. Nó sẽ cung cấp một sự hiểu biết tốt hơn cho độc giả trong tương lai.
gwaramadze

1
Các vị trí tùy ý của Newport và Cleveland đến từ tài liệu địa lý mẫu trong danh sách PyPI: pypi.python.org/pypi/geopy
Jason Parham

Tôi đã phải sửa đổi câu trả lời của Kurt Peek cho vấn đề này: Yêu cầu viết hoa:print geopy.distance.VincentyDistance(coords_1, coords_2).km 279.352901604
Jim

4
Bạn có thể nên sử dụng geopy.distance.distance(…)trong mã là bí danh của công thức khoảng cách hiện tại (= chính xác nhất). (Vincenty tại thời điểm này.)
mbirth

10
Sử dụng geopy.distance.vincenty trong các kết quả đầu ra của geopy-1.18.1: Vincenty không được dùng nữa và sẽ bị xóa trong geopy 2.0. Sử dụng geopy.distance.geodesic(hoặc mặc định geopy.distance.distance) thay vào đó, chính xác hơn và luôn hội tụ.
juanmah

88

Đối với những người (như tôi) đến đây thông qua công cụ tìm kiếm và chỉ tìm kiếm một giải pháp hoạt động tốt, tôi khuyên bạn nên cài đặt mpu. Cài đặt nó qua pip install mpu --uservà sử dụng nó như thế này để có được khoảng cách 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.

Một gói thay thế là gpxpy.

Nếu bạn không muốn phụ thuộc, bạn có thể sử dụng:

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

Gói thay thế khác là [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

Họ tuyên bố sẽ tối ưu hóa hiệu suất cho khoảng cách giữa tất cả các điểm trong hai vectơ

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

Có cách nào để thay đổi Highet đã cho của một trong những điểm không?
yigs cohen

Bạn chỉ có thể thêm chênh lệch chiều cao cho khoảng cách. Tôi sẽ không làm điều đó, mặc dù.
Martin Thoma

16

Tôi đã tìm đến một giải pháp đơn giản và mạnh mẽ hơn nhiều, đó là sử dụng geodesictừ geopygói vì bạn rất có thể sẽ sử dụng nó trong dự án của mình vì vậy không cần cài đặt thêm gói.

Đây là giải pháp của tôi:

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

địa lý


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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.