Jarak antara dua titik berdasarkan garis lintang / bujur


158

Saya mencoba menerapkan rumus ini: http://andrew.hedges.name/experiments/haversine/ Aplet ini berfungsi baik untuk dua poin yang saya uji:

masukkan deskripsi gambar di sini

Namun kode saya tidak berfungsi.

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

Jarak yang dikembalikan adalah 5447.05546147 . Mengapa?

Jawaban:


207

Sunting: Sama seperti catatan, jika Anda hanya perlu cara cepat dan mudah untuk menemukan jarak antara dua poin, saya sangat merekomendasikan menggunakan pendekatan yang dijelaskan dalam jawaban Kurt di bawah daripada menerapkan kembali Haversine - lihat posnya untuk alasan.

Jawaban ini berfokus hanya pada menjawab bug spesifik yang ditemui OP.


Itu karena dalam Python, semua fungsi trigonometri menggunakan radian , bukan derajat.

Anda dapat mengonversi angka secara manual ke radian, atau menggunakan radiansfungsi dari modul matematika:

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

Jarak sekarang mengembalikan nilai 278.545589351km yang benar.


13
ini berlaku dalam bahasa pemrograman apa pun, dan juga dalam kalkulus diferensial. menggunakan derajat adalah pengecualian, dan hanya digunakan dalam ucapan manusia.
bluesmoon

11
Dari kata bijak, formula ini mengharuskan semua derajat menjadi positif. radians(abs(52.123))harus melakukan trik ...
Richard Dunn

1
Apakah Anda yakin semua derajat (sudut?) Menjadi positif? Saya pikir ini salah. Pertimbangkan jika lat1, lon1 = 10, 10 (derajat) dan lat2, lon2 = -10, -10 (derajat). Dengan menambahkan abs () di sekitar derajat, jarak akan menjadi nol, yang tidak benar. Mungkin Anda bermaksud mengambil nilai absolut dlon dan / atau dlat, tetapi jika Anda melihat dlon, nilai dlat dalam perhitungan a, sinus adalah fungsi genap, dan cosinus kuadrat adalah fungsi genap, jadi saya tidak lihat manfaat mengambil nilai absolut dlat atau dlon, baik.
Dave LeCompte

238

Pembaruan: 04/2018: Perhatikan bahwa jarak Vincenty sudah ditinggalkan sejak GeoPy versi 1.13 - Anda harus menggunakan geopy.distance.distance () sebagai gantinya!


Jawaban di atas didasarkan pada rumus Haversine , yang mengasumsikan bumi adalah bola, yang menghasilkan kesalahan hingga sekitar 0,5% (menurut help(geopy.distance)). Jarak Vincenty menggunakan model ellipsoidal yang lebih akurat seperti WGS-84 , dan diimplementasikan dalam geopy . Sebagai contoh,

import geopy.distance

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

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

akan mencetak jarak 279.352901604kilometer menggunakan default ellipsoid WGS-84. (Anda juga dapat memilih .milesatau salah satu dari beberapa unit jarak lain).


1
Terima kasih. Bisakah Anda memperbarui jawaban Anda dengan koordinat yang saya berikan bukan Newport dan Cleveland. Ini akan memberikan pemahaman yang lebih baik kepada pembaca di masa depan.
gwaramadze

1
Lokasi sewenang-wenang Newport dan Cleveland berasal dari contoh dokumentasi geopy dalam daftar PyPI: pypi.python.org/pypi/geopy
Jason Parham

Saya harus mengubah jawaban Kurt Peek untuk ini: Kapitalisasi diperlukan:print geopy.distance.VincentyDistance(coords_1, coords_2).km 279.352901604
Jim

4
Anda mungkin harus menggunakan geopy.distance.distance(…)kode yang merupakan alias dari rumus jarak terbaik saat ini (= paling akurat). (Vincenty saat ini.)
kelahiran

10
Menggunakan geopy.distance.vincenty di geopy-1.18.1 output: Vincenty sudah usang dan akan dihapus di geopy 2.0. Gunakan geopy.distance.geodesic(atau default geopy.distance.distance) sebagai gantinya, yang lebih akurat dan selalu konvergen.
juanmah

88

Untuk orang-orang (seperti saya) datang ke sini melalui mesin pencari dan hanya mencari solusi yang bekerja di luar kotak, saya sarankan menginstal mpu. Instal via pip install mpu --userdan gunakan seperti ini untuk mendapatkan jarak haversine :

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.

Paket alternatif adalah gpxpy.

Jika Anda tidak menginginkan dependensi, Anda dapat menggunakan:

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

Paket alternatif lainnya adalah [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

Mereka mengklaim memiliki optimasi kinerja untuk jarak antara semua titik dalam dua vektor

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

Apakah ada cara untuk mengubah Highet yang diberikan dari salah satu poin?
yovel cohen

Anda bisa menambahkan perbedaan ketinggian pada jarak. Saya tidak akan melakukan itu.
Martin Thoma

16

Saya tiba di solusi yang jauh lebih sederhana dan kuat yang menggunakan geodesicdari geopypaket karena Anda akan sangat mungkin menggunakannya dalam proyek Anda sehingga tidak perlu instalasi paket tambahan.

Ini solusinya:

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

Ada beberapa cara untuk menghitung jarak berdasarkan koordinat yaitu lintang dan bujur

Pasang dan impor

from geopy import distance
from math import sin, cos, sqrt, atan2, radians
from sklearn.neighbors import DistanceMetric
import osrm
import numpy as np

Tentukan koordinat

lat1, lon1, lat2, lon2, R = 20.9467,72.9520, 21.1702, 72.8311, 6373.0
coordinates_from = [lat1, lon1]
coordinates_to = [lat2, lon2]

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

Menggunakan haversine dengan 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))

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

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

Keluaran

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.