Python中的Haversine公式(两个GPS点之间的轴承和距离)


119

问题

我想知道如何获取2个GPS点之间距离和方位。我研究了haversine配方。有人告诉我,我也可以使用相同的数据找到轴承。

编辑

一切正常,但是轴承还不能正常工作。轴承输出为负,但应在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 "--------------------"

可以在codecodex.com/wiki/…中找到Python haversine的实现。但是,对于短距离计算,存在非常简单的方法。现在,您的最大距离是多少?您是否可以在某些本地笛卡尔坐标系中获取坐标?


1
@詹姆士·戴森(James Dyson):距离约15公里,创建圆没有任何意义。我的建议:首先找出具有欧几里德距离的解决方案!这将为您提供一个可行的解决方案,然后如果您的距离要长得多,请稍后调整您的应用程序。谢谢

1
@詹姆士·戴森(James Dyson):如果您的上述评论针对我(以及我先前的建议),那么答案肯定是(也很“琐碎”)。我也许可以给出一些示例代码,但是它不会利用三角函数,而是几何图形(因此,我不确定它是否对您有帮助。您对向量的概念是否完全熟悉?可以使用向量以最直接的方式处理方向)。

1
atan2(sqrt(a), sqrt(1-a))asin(sqrt(a))
user102008 2011年

Answers:


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相乘-效果相同,但更能说明问题。
休·博斯韦尔

4
您可以,但是如果您要说的话,import math则必须指定math.pimath.sin等等。这样from math import *您就可以直接访问所有模块内容。看看在Python教程“命名空间”(如docs.python.org/tutorial/modules.html
迈克尔邓恩

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 km到赤道的6378.137 km的变化
ldmtwo

3
这个错误对您的应用程序真的重要吗?cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
Tejas Kale

8

还有一个向量化的实现,该实现允许使用4个numpy数组代替标量值作为坐标:

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

方位角计算不正确,您需要将输入交换为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

4

您可以尝试以下方法:

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

如何在Django的ORM查询中使用它?
Gocht 2015年

3

这是@Michael Dunn给出的Haversine公式的numpy矢量化实现,比大型矢量提高了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
我认为这只是:轴承=轴承百分比360
Holger Bille

1

默认情况下,atan2中的Y是第一个参数。这是文档。您将需要切换输入以获得正确的方位角。

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

这是两个用于计算距离和方位的函数,它们基于先前消息和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)

与上面的所有其他方法相比,此方法可提供其他结果!
蛇怪
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.