รับระยะทางระหว่างจุดสองจุดโดยอิงตามละติจูด / ลองจิจูด


157

ฉันพยายามใช้สูตรนี้: 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 ทำไม?

คำตอบ:


206

แก้ไข:เช่นเดียวกับบันทึกหากคุณต้องการวิธีหาระยะห่างระหว่างสองจุดอย่างรวดเร็วและง่ายดายฉันขอแนะนำให้ใช้วิธีการที่อธิบายไว้ในคำตอบของ Kurtด้านล่างแทนการใช้ 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กิโลเมตร


13
สิ่งนี้เป็นจริงในภาษาการเขียนโปรแกรมใด ๆ และในแคลคูลัสต่างกัน ใช้องศาเป็นข้อยกเว้นและใช้ในการพูดของมนุษย์เท่านั้น
bluesmoon

11
คำที่ฉลาดสูตรนี้ต้องใช้ทุกองศาเป็นบวก radians(abs(52.123))ควรทำเคล็ดลับ ...
ริชาร์ดดันน์

1
คุณแน่ใจเกี่ยวกับองศาทั้งหมดหรือไม่? ฉันคิดว่ามันผิด พิจารณาว่า lat1, lon1 = 10, 10 (องศา) และ lat2, lon2 = -10, -10 (องศา) ด้วยการเพิ่ม abs () รอบองศาระยะทางจะเป็นศูนย์ซึ่งไม่ถูกต้อง บางทีคุณอาจใช้ค่าสัมบูรณ์ของ dlon และ / หรือ dlat แต่ถ้าคุณดูที่ dlon, ค่า dlat ในการคำนวณ a, sine คือฟังก์ชันคู่และ cosine squared เป็นฟังก์ชันคู่ดังนั้นฉันไม่ เห็นประโยชน์ใด ๆ ในการรับค่าสัมบูรณ์ของ dlat หรือ dlon เช่นกัน
Dave LeCompte

238

อัปเดต: 04/2018:โปรดทราบว่าระยะทาง Vincenty นั้นไม่ได้รับการแก้ไขตั้งแต่รุ่น GeoPy 1.13 - คุณควรใช้ geopy.distance.distance () แทน!


คำตอบข้างต้นจะขึ้นอยู่กับสูตร Haversineซึ่งถือว่าโลกเป็นทรงกลมซึ่งส่งผลให้เกิดข้อผิดพลาดสูงถึงประมาณ 0.5% (ตามhelp(geopy.distance)) ระยะ Vincentyใช้แบบจำลองรูปวงรีที่ถูกต้องมากขึ้นเช่นWGS-84และจะดำเนินการในgeopy ตัวอย่างเช่น,

import geopy.distance

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

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

จะพิมพ์ระยะทาง279.352901604กิโลเมตรโดยใช้รูปทรงรีเริ่มต้น WGS-84 (คุณยังสามารถเลือก.milesหรือหนึ่งในหน่วยระยะทางอื่น ๆ อีกมากมาย)


1
ขอบคุณ คุณช่วยอัพเดตคำตอบของคุณด้วยพิกัดที่ฉันให้ไว้แทนนิวพอร์ตและคลีฟแลนด์ได้ไหม มันจะให้ความเข้าใจที่ดีขึ้นกับผู้อ่านในอนาคต
gwaramadze

1
สถานที่โดยพลการของนิวพอร์ตและคลีฟแลนด์มาจากเอกสารตัวอย่าง geopy ใน PyPI รายการ: pypi.python.org/pypi/geopy
เจสันพารัม

ฉันต้องแก้ไขคำตอบของ Kurt Peek ต่อสิ่งนี้: ต้องใช้ตัวพิมพ์ใหญ่:print geopy.distance.VincentyDistance(coords_1, coords_2).km 279.352901604
จิม

4
คุณควรใช้geopy.distance.distance(…)รหัสซึ่งเป็นนามแฝงของสูตรระยะทางที่ดีที่สุด (= ถูกต้องที่สุด) (ขณะนี้อยู่ในขณะนั้นอีก
ยี่สิบ

10
การใช้ geopy.distance.vincenty ในเอาท์พุท geopy-1.18.1: Vincenty เลิกใช้แล้วและจะถูกลบใน geopy 2.0 ใช้geopy.distance.geodesic(หรือค่าเริ่มต้นgeopy.distance.distance) แทนซึ่งมีความแม่นยำมากกว่าและลู่เข้าหากันเสมอ
juanmah

88

สำหรับคน (เช่นฉัน) 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][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

พวกเขาอ้างว่ามีการเพิ่มประสิทธิภาพการปฏิบัติงานสำหรับระยะทางระหว่างจุดทั้งหมดในสองเวกเตอร์

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

มีวิธีในการเปลี่ยน Highet ที่กำหนดจากหนึ่งในคะแนนหรือไม่
yovel โคเฮน

คุณสามารถเพิ่มความแตกต่างของความสูงกับระยะทางได้ ฉันจะไม่ทำอย่างนั้น
Martin Thoma

16

ฉันมาถึงโซลูชันที่เรียบง่ายและมีประสิทธิภาพมากซึ่งใช้geodesicจากgeopyแพ็คเกจเนื่องจากคุณมีแนวโน้มที่จะใช้มันในโครงการของคุณต่อไปดังนั้นจึงไม่จำเป็นต้องติดตั้งแพ็คเกจเพิ่มเติม

นี่คือทางออกของฉัน:

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

geopy


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)

0

มีหลายวิธีในการคำนวณระยะทางตามพิกัดเช่นละติจูดและลองจิจูด

ติดตั้งและนำเข้า

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]

การใช้ haversine

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)

ใช้ haversine กับ sklearn

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

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)

ใช้ geopy

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.