[英]Getting distance between two points based on latitude/longitude
我嘗試實現這個公式: http://andrew.hedges.name/experiments/haversine/ aplet 對我正在測試的兩點有好處:
然而我的代碼不起作用。
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 。 為什么?
更新:04/2018: Vincenty 距離 自 GeoPy 1.13 版以來已棄用- 您應該改用geopy.distance.distance()
!
上面的答案基於Haversine 公式,它假設地球是一個球體,導致誤差高達 0.5% 左右(根據help(geopy.distance)
)。 Vincenty distance使用更精確的橢球模型,例如WGS-84 ,並在geopy中實現。 例如,
import geopy.distance
coords_1 = (52.2296756, 21.0122287)
coords_2 = (52.406374, 16.9251681)
print geopy.distance.geodesic(coords_1, coords_2).km
將使用默認橢球 WGS-84 打印279.352901604
公里的距離。 (您也可以選擇.miles
或其他幾個距離單位之一)。
編輯:請注意,如果您只需要一種快速簡便的方法來查找兩點之間的距離,我強烈建議您使用下面庫爾特的回答中描述的方法,而不是重新實現 Haversine - 請參閱他的帖子了解基本原理。
該答案僅側重於回答 OP 遇到的特定錯誤。
這是因為在 Python 中,所有三角函數都使用弧度,而不是度數。
您可以手動將數字轉換為弧度,或使用數學模塊中的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.545589351
公里。
對於通過搜索引擎來到這里並只是尋找開箱即用的解決方案的人(比如我),我建議安裝mpu
。 通過pip install mpu --user
安裝它並像這樣使用它來獲取半正弦距離:
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
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])
我得到了一個更簡單和強大的解決方案,它使用geopy
包中的geodesic
,因為無論如何你很可能在你的項目中使用它,所以不需要額外的包安裝。
這是我的解決方案:
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
有多種方法可以根據坐標計算距離,即緯度和經度
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]
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)
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_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
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)
您可以使用Uber 的 H3 、 point_dist()
函數來計算兩個 (lat, lng) 點之間的球面距離。 我們可以設置返回單位('km'、'm' 或 'rads')。 默認單位是公里。
例子 :
import h3
coords_1 = (52.2296756, 21.0122287)
coords_2 = (52.406374, 16.9251681)
distance = h3.point_dist(coords_1, coords_2, unit='m') # to get distance in meters
希望這會有用!
在 2022 年,人們可以發布使用更新的 javascript 庫解決此問題的實時 javascript 代碼。 一般的好處是用戶可以在現代設備上運行的網頁上運行並查看結果。
// Using WGS84 ellipsoid model for computation var geod84 = geodesic.Geodesic.WGS84; // Input data lat1 = 52.2296756; lon1 = 21.0122287; lat2 = 52.406374; lon2 = 16.9251681; // Do the classic `geodetic inversion` computatioin geod84inv = geod84.Inverse(lat1, lon1, lat2, lon2); // Present the solution (only the geodetic distance) console.log("The distance is " + (geod84inv.s12/1000).toFixed(5) + " km.");
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/geographiclib-geodesic@2.0.0/geographiclib-geodesic.min.js"> </script>
在 2022 年,可以使用更新的 python 庫,即geographiclib
發布混合的javascript+python
代碼來解決這個問題。 一般的好處是用戶可以在現代設備上運行的網頁上運行並查看結果。
async function main(){ let pyodide = await loadPyodide(); await pyodide.loadPackage(["micropip"]); console.log(pyodide.runPythonAsync(` import micropip await micropip.install('geographiclib') from geographiclib.geodesic import Geodesic lat1 = 52.2296756; lon1 = 21.0122287; lat2 = 52.406374; lon2 = 16.9251681; ans = Geodesic.WGS84.Inverse(lat1, lon1, lat2, lon2) dkm = ans["s12"] / 1000 print("Geodesic solution", ans) print(f"Distance = {dkm:.4f} km.") `)); } main();
<script src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js"></script>
最簡單的方法是使用 hasrsine 包。
import haversine as hs
coord_1 = (lat, lon)
coord_2 = (lat, lon)
x = hs.haversine(coord_1,coord_2)
print(f'The distance is {x} km')
另一個有趣的使用混合javascript+python
通過pyodide
和webassembly
實現來獲得使用 Python 庫pandas+geographiclib
的解決方案也是可行的。 我使用pandas
做了額外的努力來准備輸入數據,當輸出可用時,將它們附加到solution
列。 Pandas 為滿足常見需求的輸入/輸出提供了許多有用的功能。 它的toHtml
方法很方便在網頁上呈現最終的解決方案
編輯我發現此答案中的代碼在某些 iphone 和 ipad 設備上執行不成功。 但在較新的中端 Android 設備上運行良好。 我會找到一種方法來糾正這個問題並盡快更新。
我的旁注,我知道我的答案不像其他答案那樣直接回答 OP 問題。 但最近外界表示,StackOvereflow 中的很多答案已經過時,並試圖引導人們遠離這里。
async function main(){ let pyodide = await loadPyodide(); await pyodide.loadPackage(["pandas", "micropip"]); console.log(pyodide.runPythonAsync(` import micropip import pandas as pd import js print("Pandas version: " + pd.__version__) await micropip.install('geographiclib') from geographiclib.geodesic import Geodesic import geographiclib as gl print("Geographiclib version: " + gl.__version__) data = {'Description': ['Answer to the question', 'Bangkok to Tokyo'], 'From_long': [21.0122287, 100.6], 'From_lat': [52.2296756, 13.8], 'To_long': [16.9251681, 139.76], 'To_lat': [52.406374, 35.69], 'Distance_km': [0, 0]} df1 = pd.DataFrame(data) collist = ['Description','From_long','From_lat','To_long','To_lat'] div2 = js.document.createElement("div") div2content = df1.to_html(buf=None, columns=collist, col_space=None, header=True, index=True) div2.innerHTML = div2content js.document.body.append(div2) arr="<i>by Swatchai</i>" def dkm(frLat,frLon,toLat,toLon): print("frLon,frLat,toLon,toLat:", frLon, "|", frLat, "|", toLon, "|", toLat) dist = Geodesic.WGS84.Inverse(frLat, frLon, toLat, toLon) return dist["s12"] / 1000 collist = ['Description','From_long','From_lat','To_long','To_lat','Distance_km'] dist = [] for ea in zip(df1['From_lat'].values, df1['From_long'].values, df1['To_lat'].values, df1['To_long'].values): ans = dkm(*ea) print("ans=", ans) dist.append(ans) df1['Distance_km'] = dist # Update content div2content = df1.to_html(buf=None, columns=collist, col_space=None, header=True, index=False) div2.innerHTML = div2content js.document.body.append(div2) # Using Haversine Formula from math import sin, cos, sqrt, atan2, radians, asin # approximate radius of earth in km from wikipedia R = 6371 lat1 = radians(52.2296756) lon1 = radians(21.0122287) lat2 = radians(52.406374) lon2 = radians(16.9251681) # https://en.wikipedia.org/wiki/Haversine_formula def hav(angrad): return (1-cos(angrad))/2 h = hav(lat2-lat1)+cos(lat2)*cos(lat1)*hav(lon2-lon1) dist2 = 2*R*asin(sqrt(h)) print(f"Distance by haversine formula = {dist2:8.6f} km.") `)); } main();
<script src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js"></script> Pyodide implementation<br>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.