Công thức Haversine bằng Python (Vòng bi và Khoảng cách giữa hai điểm GPS)


119

Vấn đề

Tôi muốn biết làm thế nào để có được khoảng cách và mang giữa 2 điểm GPS . Tôi đã nghiên cứu về công thức hasrsine. Có người nói với tôi rằng tôi cũng có thể tìm thấy ổ trục bằng cách sử dụng cùng một dữ liệu.

Biên tập

Mọi thứ đều hoạt động tốt nhưng ổ trục vẫn chưa hoạt động bình thường. Vòng bi đầu ra âm nhưng phải nằm trong khoảng 0 - 360 độ. Dữ liệu tập hợp nên làm cho ổ trục ngang 96.02166666666666 và là:

Start point: 53.32055555555556 , -1.7297222222222221   
Bearing:  96.02166666666666  
Distance: 2 km  
Destination point: 53.31861111111111, -1.6997222222222223  
Final bearing: 96.04555555555555

Đây là mã mới của tôi:

from math import *

Aaltitude = 2000
Oppsite  = 20000

lat1 = 53.32055555555556
lat2 = 53.31861111111111
lon1 = -1.7297222222222221
lon2 = -1.6997222222222223

lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

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))
Base = 6371 * c


Bearing =atan2(cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1), sin(lon2-lon1)*cos(lat2)) 

Bearing = degrees(Bearing)
print ""
print ""
print "--------------------"
print "Horizontal Distance:"
print Base
print "--------------------"
print "Bearing:"
print Bearing
print "--------------------"


Base2 = Base * 1000
distance = Base * 2 + Oppsite * 2 / 2
Caltitude = Oppsite - Aaltitude

a = Oppsite/Base
b = atan(a)
c = degrees(b)

distance = distance / 1000

print "The degree of vertical angle is:"
print c
print "--------------------"
print "The distance between the Balloon GPS and the Antenna GPS is:"
print distance
print "--------------------"

Triển khai hasrsine trong Python có thể được tìm thấy codecodex.com/wiki/… . Tuy nhiên đối với các tính toán khoảng cách ngắn tồn tại những cách rất đơn giản. Bây giờ, khoảng cách tối đa của bạn dự kiến ​​là bao nhiêu? Bạn có thể có được điều phối của mình trong một số hệ thống điều phối cartesian địa phương không?
ăn


1
@James Dyson: với khoảng cách như 15km, vòng tròn tạo không tính gì cả. Đề nghị của tôi: trước tiên hãy tìm ra giải pháp với khoảng cách euclide! Điều đó sẽ cung cấp cho bạn một giải pháp làm việc và sau đó nếu khoảng cách của bạn sẽ dài hơn nhiều, thì hãy điều chỉnh ứng dụng của bạn. Cảm ơn
ăn

1
@James Dyson: Nếu nhận xét ở trên của bạn nhắm đến tôi (và theo gợi ý trước đó của tôi), câu trả lời chắc chắn là (và cũng khá 'tầm thường'). Tôi có thể đưa ra một số mã ví dụ, nhưng nó sẽ không sử dụng lượng giác, thay vì hình học (vì vậy tôi không chắc liệu nó có giúp ích cho bạn không. Bạn có quen thuộc với khái niệm vectơ không? Trong trường hợp của bạn, vị trí và hướng có thể được xử lý theo cách đơn giản nhất với vectơ).
ăn

1
atan2(sqrt(a), sqrt(1-a))cũng giống nhưasin(sqrt(a))
user102008

Câu trả lời:


241

Đây là phiên bản Python:

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

def haversine(lon1, lat1, lon2, lat2):
    """
    Calculate the great circle distance between two points 
    on the earth (specified in decimal degrees)
    """
    # convert decimal degrees to radians 
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

    # haversine formula 
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 
    r = 6371 # Radius of earth in kilometers. Use 3956 for miles
    return c * r

10
Có thể sử dụng hàm math.radians () thay vì nhân với số pi / 180 - hiệu quả tương tự, nhưng tự ghi lại nhiều hơn một chút.
Hugh Bothwell

4
Bạn có thể, nhưng nếu bạn nói import maththì bạn phải chỉ định math.pi, math.sinv.v. Với from math import *bạn, bạn có thể truy cập trực tiếp vào tất cả nội dung mô-đun. Kiểm tra "không gian tên" trong hướng dẫn về python (chẳng hạn như docs.python.org/tutorial/modules.html )
Michael Dunn

2
Làm thế nào bạn sử dụng atan2 (sqrt (a), sqrt (1-a)) thay vì chỉ asin (sqrt (a))? Atan2 có chính xác hơn trong trường hợp này không?
Eyal

4
Nếu bán kính Trái đất trung bình được định nghĩa là 6371 km, thì đó chính là tương đương với 3959 dặm, không 3956 dặm. Xem Bán kính trung bình toàn cầu để biết các cách khác nhau để tính các giá trị này.
ekhumoro

3
cái gì thế này trở lại? Vòng bi hay khoảng cách?
AesculusMaximus

11

Hầu hết các câu trả lời này là "làm tròn" bán kính trái đất. Nếu bạn kiểm tra chúng với các máy tính khoảng cách khác (chẳng hạn như geopy), các chức năng này sẽ bị tắt.

Điều này hoạt động tốt:

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

def haversine(lat1, lon1, lat2, lon2):

      R = 3959.87433 # this is in miles.  For Earth radius in kilometers use 6372.8 km

      dLat = radians(lat2 - lat1)
      dLon = radians(lon2 - lon1)
      lat1 = radians(lat1)
      lat2 = radians(lat2)

      a = sin(dLat/2)**2 + cos(lat1)*cos(lat2)*sin(dLon/2)**2
      c = 2*asin(sqrt(a))

      return R * c

# Usage
lon1 = -103.548851
lat1 = 32.0004311
lon2 = -103.6041946
lat2 = 33.374939

print(haversine(lat1, lon1, lat2, lon2))

2
Điều này chính xác hơn nhiều so với các ví dụ ở trên!
Alex van Es

Điều này không giải quyết được sự biến đổi của R. 6356,752 km tại hai cực thành 6378,177 km tại đường xích đạo
ldmtwo

3
Lỗi đó có thực sự quan trọng đối với ứng dụng của bạn không? cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
Tejas Kale

8

Ngoài ra còn có một triển khai vectơ hóa , cho phép sử dụng 4 mảng numpy thay vì các giá trị vô hướng cho tọa độ:

def distance(s_lat, s_lng, e_lat, e_lng):

   # approximate radius of earth in km
   R = 6373.0

   s_lat = s_lat*np.pi/180.0                      
   s_lng = np.deg2rad(s_lng)     
   e_lat = np.deg2rad(e_lat)                       
   e_lng = np.deg2rad(e_lng)  

   d = np.sin((e_lat - s_lat)/2)**2 + np.cos(s_lat)*np.cos(e_lat) * np.sin((e_lng - s_lng)/2)**2

   return 2 * R * np.arcsin(np.sqrt(d))

4

Tính toán ổ trục không chính xác, bạn cần hoán đổi các đầu vào thành atan2.

    bearing = atan2(sin(long2-long1)*cos(lat2), cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(long2-long1))
    bearing = degrees(bearing)
    bearing = (bearing + 360) % 360

Điều này sẽ cung cấp cho bạn vòng bi chính xác.


Tôi thực sự đang đấu tranh để hiểu làm thế nào những phương trình này được suy ra khi tôi đang đọc một bài báo. Bạn đã cho tôi một gợi ý: haversine formulalần đầu tiên tôi nghe thấy điều này, cảm ơn bạn.
arilwan

4

Bạn có thể thử những cách sau:

from haversine import haversine
haversine((45.7597, 4.8422),(48.8567, 2.3508), unit='mi')
243.71209416020253

Làm thế nào điều này có thể được sử dụng trong truy vấn ORM của Django?
Gocht

3

Đây là một triển khai vectơ phức tạp của Công thức Haversine do @Michael Dunn đưa ra, cải thiện 10-50 lần so với các vectơ lớn.

from numpy import radians, cos, sin, arcsin, sqrt

def haversine(lon1, lat1, lon2, lat2):
    """
    Calculate the great circle distance between two points 
    on the earth (specified in decimal degrees)
    """

    #Convert decimal degrees to Radians:
    lon1 = np.radians(lon1.values)
    lat1 = np.radians(lat1.values)
    lon2 = np.radians(lon2.values)
    lat2 = np.radians(lat2.values)

    #Implementing Haversine Formula: 
    dlon = np.subtract(lon2, lon1)
    dlat = np.subtract(lat2, lat1)

    a = np.add(np.power(np.sin(np.divide(dlat, 2)), 2),  
                          np.multiply(np.cos(lat1), 
                                      np.multiply(np.cos(lat2), 
                                                  np.power(np.sin(np.divide(dlon, 2)), 2))))
    c = np.multiply(2, np.arcsin(np.sqrt(a)))
    r = 6371

    return c*r

2

Bạn có thể giải quyết vấn đề ổ trục âm bằng cách thêm 360 °. Thật không may, điều này có thể dẫn đến vòng bi lớn hơn 360 ° đối với vòng bi dương. Đây là một ứng cử viên sáng giá cho toán tử modulo, vì vậy, nhìn chung, bạn nên thêm dòng

Bearing = (Bearing + 360) % 360

ở cuối phương pháp của bạn.


1
Tôi nghĩ đó chỉ là: Bearing = Bearing% 360
Holger Bille

1

Theo mặc định, Y trong atan2 là tham số đầu tiên. Đây là tài liệu . Bạn sẽ cần chuyển đổi các đầu vào để có được góc chịu lực chính xác.

bearing = atan2(sin(lon2-lon1)*cos(lat2), cos(lat1)*sin(lat2)in(lat1)*cos(lat2)*cos(lon2-lon1))
bearing = degrees(bearing)
bearing = (bearing + 360) % 360


0

Đây là hai hàm để tính toán khoảng cách và mang, dựa trên mã trong các tin nhắn trước và https://gist.github.com/jeromer/2005586 (đã thêm kiểu tuple cho các điểm địa lý ở định dạng vĩ độ, vĩ độ cho cả hai hàm để rõ ràng ). Tôi đã thử nghiệm cả hai chức năng và chúng dường như hoạt động bình thường.

#coding:UTF-8
from math import radians, cos, sin, asin, sqrt, atan2, degrees

def haversine(pointA, pointB):

    if (type(pointA) != tuple) or (type(pointB) != tuple):
        raise TypeError("Only tuples are supported as arguments")

    lat1 = pointA[0]
    lon1 = pointA[1]

    lat2 = pointB[0]
    lon2 = pointB[1]

    # convert decimal degrees to radians 
    lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2]) 

    # haversine formula 
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 
    r = 6371 # Radius of earth in kilometers. Use 3956 for miles
    return c * r


def initial_bearing(pointA, pointB):

    if (type(pointA) != tuple) or (type(pointB) != tuple):
        raise TypeError("Only tuples are supported as arguments")

    lat1 = radians(pointA[0])
    lat2 = radians(pointB[0])

    diffLong = radians(pointB[1] - pointA[1])

    x = sin(diffLong) * cos(lat2)
    y = cos(lat1) * sin(lat2) - (sin(lat1)
            * cos(lat2) * cos(diffLong))

    initial_bearing = atan2(x, y)

    # Now we have the initial bearing but math.atan2 return values
    # from -180° to + 180° which is not what we want for a compass bearing
    # The solution is to normalize the initial bearing as shown below
    initial_bearing = degrees(initial_bearing)
    compass_bearing = (initial_bearing + 360) % 360

    return compass_bearing

pA = (46.2038,6.1530)
pB = (46.449, 30.690)

print haversine(pA, pB)

print initial_bearing(pA, pB)

phương pháp này cho kết quả khác với tất cả các phương pháp khác ở trên!
basilisk
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.