สูตร Haversine ใน Python (แบริ่งและระยะห่างระหว่างจุด GPS สองจุด)


120

ปัญหา

ผมอยากจะทราบวิธีการที่จะได้รับระยะทางและแบริ่งระหว่าง 2 จุดจีพีเอส ฉันได้ค้นคว้าเกี่ยวกับสูตรฮาเวอร์ซีน มีคนบอกฉันว่าฉันสามารถค้นหาตลับลูกปืนได้โดยใช้ข้อมูลเดียวกัน

แก้ไข

ทุกอย่างทำงานได้ดี แต่ตลับลูกปืนยังใช้งานไม่ได้ แบริ่งส่งผลลบ แต่ควรอยู่ระหว่าง 0-360 องศา ข้อมูลชุดควรทำให้แบริ่งแนวนอน96.02166666666666 และเป็น:

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

นี่คือรหัสใหม่ของฉัน:

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

การใช้งาน Python haversine สามารถพบได้ที่codecodex.com/wiki/… . อย่างไรก็ตามสำหรับการคำนวณระยะทางสั้นมีวิธีง่ายๆ ตอนนี้ระยะทางสูงสุดที่คุณคาดไว้คือเท่าไร? คุณสามารถหาพิกัดของคุณในระบบประสานงานคาร์ทีเซียนในพื้นที่ได้หรือไม่?
ทาน

การใช้งานบางอย่างใน python: - code.activestate.com/recipes/… - platoscave.net/blog/2009/oct/5/…
Fábio Diniz

1
@ James Dyson: ด้วยระยะทาง 15 กม. แวดวงการสร้างสรรค์ไม่นับอะไรเลย ข้อเสนอแนะของฉัน: หาวิธีแก้ปัญหาด้วยระยะทางแบบยุคลิดก่อน! นั่นจะเป็นวิธีแก้ปัญหาในการทำงานและหลังจากนั้นหากระยะทางของคุณยาวขึ้นมากให้ปรับแอปพลิเคชันของคุณ ขอบคุณ
ทาน

1
@ James Dyson: หากความคิดเห็นข้างต้นของคุณมุ่งเป้าไปที่ฉัน (และตามคำแนะนำก่อนหน้านี้ของฉัน) คำตอบก็แน่นอน (และค่อนข้าง 'เล็กน้อย' เช่นกัน) ฉันอาจจะให้โค้ดตัวอย่างได้บ้าง แต่จะไม่ใช้ตรีโกณมิติแทนเรขาคณิต (ฉันไม่แน่ใจว่ามันจะช่วยคุณได้หรือไม่คุณคุ้นเคยกับแนวคิดของเวกเตอร์หรือไม่ในตำแหน่งกรณีของคุณและ ทิศทางสามารถจัดการได้อย่างตรงไปตรงมาที่สุดด้วยเวกเตอร์)
ทาน

1
atan2(sqrt(a), sqrt(1-a))เหมือนกับasin(sqrt(a))
user102008

คำตอบ:


241

นี่คือเวอร์ชัน 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
สามารถใช้ฟังก์ชัน math.radians () แทนการคูณด้วย pi / 180 - เอฟเฟกต์เดียวกัน แต่มีการจัดทำเอกสารด้วยตนเองมากขึ้นเล็กน้อย
Hugh Bothwell

4
คุณสามารถ แต่ถ้าคุณพูดimport mathแล้วคุณจะต้องระบุmath.pi, math.sinฯลฯ ด้วยfrom math import *คุณจะได้รับการเข้าถึงโดยตรงไปเนื้อหาทั้งหมดโมดูล ดู "เนมสเปซ" ในบทช่วยสอน Python (เช่นdocs.python.org/tutorial/modules.html )
Michael Dunn

2
ทำไมคุณถึงใช้ atan2 (sqrt (a), sqrt (1-a)) แทนแค่ asin (sqrt (a))? atan2 แม่นยำกว่าในกรณีนี้หรือไม่?
Eyal

4
ถ้ากำหนดรัศมีโลกเฉลี่ยเป็น 6371 กม. ก็เท่ากับว่า 3959 ไมล์ไม่ใช่ 3956 ไมล์ ดูรัศมีเฉลี่ยทั่วโลกสำหรับวิธีต่างๆในการคำนวณค่าเหล่านี้
ekhumoro

3
สิ่งนี้กลับมา? ลูกปืนหรือระยะ?
AesculusMaximus

11

คำตอบเหล่านี้ส่วนใหญ่คือ "การปัดเศษ" รัศมีของโลก หากคุณตรวจสอบสิ่งเหล่านี้กับเครื่องคำนวณระยะทางอื่น ๆ (เช่น geopy) ฟังก์ชันเหล่านี้จะปิดลง

สิ่งนี้ใช้ได้ดี:

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
อันนี้แม่นยำกว่าตัวอย่างด้านบน!
Alex van Es

สิ่งนี้ไม่ได้กล่าวถึงการเปลี่ยนแปลงของ R 6356.752 กม. ที่เสาถึง 6378.137 กม. ที่เส้นศูนย์สูตร
ldmtwo

3
ข้อผิดพลาดนั้นสำคัญสำหรับแอปพลิเคชันของคุณหรือไม่? cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
Tejas Kale

8

นอกจากนี้ยังมีการใช้งาน vectorizedซึ่งอนุญาตให้ใช้อาร์เรย์ตัวเลข 4 ตัวแทนค่าสเกลาร์สำหรับพิกัด:

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

5

การคำนวณแบริ่งไม่ถูกต้องคุณต้องสลับอินพุตเป็น 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

สิ่งนี้จะทำให้คุณมีตลับลูกปืนที่ถูกต้อง


ฉันกำลังดิ้นรนเพื่อทำความเข้าใจว่าสมการเหล่านี้ได้มาอย่างไรขณะที่ฉันกำลังอ่านบทความ คุณให้ตัวชี้haversine formulaฉัน: ครั้งแรกที่ฉันได้ยินสิ่งนี้ขอบคุณ
arilwan

นี่ถูกต้องและ mod 360 ก็น่าสัมผัส
Marc Compere

4

คุณสามารถลองทำสิ่งต่อไปนี้:

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

สิ่งนี้สามารถใช้ในการสืบค้น ORM ของ Django ได้อย่างไร
Gocht

3

นี่คือการใช้งานเวกเตอร์ที่เป็นเวกเตอร์ของสูตร Haversine ที่มอบให้โดย @Michael Dunn ซึ่งช่วยปรับปรุงเวกเตอร์ขนาดใหญ่ 10-50 เท่า

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

คุณสามารถแก้ปัญหาแบริ่งลบได้โดยเพิ่ม 360 ° น่าเสียดายที่อาจส่งผลให้ตลับลูกปืนมีขนาดใหญ่กว่า 360 °สำหรับตลับลูกปืนเชิงบวก นี่เป็นตัวเลือกที่ดีสำหรับตัวดำเนินการโมดูโลดังนั้นคุณควรเพิ่มบรรทัดทั้งหมด

Bearing = (Bearing + 360) % 360

ในตอนท้ายของวิธีการของคุณ


1
ฉันคิดว่ามันเป็นแค่: Bearing = Bearing% 360
Holger Bille

1

Y ใน atan2 เป็นพารามิเตอร์แรกโดยค่าเริ่มต้น นี่คือเอกสาร คุณจะต้องเปลี่ยนอินพุตของคุณเพื่อให้ได้มุมแบริ่งที่ถูกต้อง

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

1

อ้างอิงลิงค์นี้: /gis/84885/whats-the-difference-between-vincenty-and-great-circle-distance-calculations

สิ่งนี้ทำให้ได้ระยะทางสองทาง พวกเขาคือ Haversine และ Vincentys จากการวิจัยของฉันฉันรู้ว่า Vincentys นั้นค่อนข้างแม่นยำ ใช้คำสั่งนำเข้าเพื่อดำเนินการ


0

ต่อไปนี้เป็นฟังก์ชันสองอย่างในการคำนวณระยะทางและแบริ่งซึ่งขึ้นอยู่กับรหัสในข้อความก่อนหน้าและ https://gist.github.com/jeromer/2005586 (เพิ่มประเภททูเพิลสำหรับจุดทางภูมิศาสตร์ในรูปแบบ lat, lon สำหรับทั้งสองฟังก์ชันเพื่อความชัดเจน ) ฉันทดสอบทั้งสองฟังก์ชั่นและดูเหมือนว่าจะทำงานได้ถูกต้อง

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

วิธีนี้ให้ผลลัพธ์ที่เหนือกว่าวิธีอื่น ๆ ทั้งหมดข้างต้น!
บาซิลิสก์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.