How can I quickly estimate the distance between two (latitude, longitude) points? - python

I want to be able to get a estimate of the distance between two (latitude, longitude) points. I want to undershoot, as this will be for A* graph search and I want it to be fast. The points will be at most 800 km apart.

The answers to Haversine Formula in Python (Bearing and Distance between two GPS points) provide Python implementations that answer your question.
Using the implementation below I performed 100,000 iterations in less than 1 second on an older laptop. I think for your purposes this should be sufficient. However, you should profile anything before you optimize for performance.
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))
# Radius of earth in kilometers is 6371
km = 6371* c
return km
To underestimate haversine(lat1, long1, lat2, long2) * 0.90 or whatever factor you want. I don't see how introducing error to your underestimation is useful.

Since the distance is relatively small, you can use the equirectangular distance approximation. This approximation is faster than using the Haversine formula. So, to get the distance from your reference point (lat1, lon1) to the point you're testing (lat2, lon2) use the formula below:
from math import sqrt, cos, radians
R = 6371 # radius of the earth in km
x = (radians(lon2) - radians(lon1)) * cos(0.5 * (radians(lat2) + radians(lat1)))
y = radians(lat2) - radians(lat1)
d = R * sqrt(x*x + y*y)
Since R is in km, the distance d will be in km.
Reference: http://www.movable-type.co.uk/scripts/latlong.html

One idea for speed is to transform the long/lat coordinated into 3D (x,y,z) coordinates. After preprocessing the points, use the Euclidean distance between the points as a quickly computed undershoot of the actual distance.

If the distance between points is relatively small (meters to few km range)
then one of the fast approaches could be
from math import cos, sqrt
def qick_distance(Lat1, Long1, Lat2, Long2):
x = Lat2 - Lat1
y = (Long2 - Long1) * cos((Lat2 + Lat1)*0.00872664626)
return 111.319 * sqrt(x*x + y*y)
Lat, Long are in radians, distance in km.
Deviation from Haversine distance is in the order of 1%, while the speed gain is more than ~10x.
0.00872664626 = 0.5 * pi/180,
111.319 - is the distance that corresponds to 1degree at Equator, you could replace it with your median value like here
https://www.cartographyunchained.com/cgsta1/
or replace it with a simple lookup table.

For maximal speed, you could create something like a rainbow table for coordinate distances. It sounds like you already know the area that you are working with, so it seems like pre-computing them might be feasible. Then, you could load the nearest combination and just use that.
For example, in the continental United States, the longitude is a 55 degree span and latitude is 20, which would be 1100 whole number points. The distance between all the possible combinations is a handshake problem which is answered by (n-1)(n)/2 or about 600k combinations. That seems pretty feasible to store and retrieve. If you provide more information about your requirements, I could be more specific.

You can use cdist from scipy spacial distance class:
For example:
from scipy.spatial.distance import cdist
df1_latlon = df1[['lat','lon']]
df2_latlon = df2[['lat', 'lon']]
distanceCalc = cdist(df1_latlon, df2_latlon, metric=haversine)

To calculate a haversine distance between 2 points u can simply use mpu.haversine_distance() library, like this:
>>> import mpu
>>> munich = (48.1372, 11.5756)
>>> berlin = (52.5186, 13.4083)
>>> round(mpu.haversine_distance(munich, berlin), 1)
>>> 504.2

Please use the following code.
def distance(lat1, lng1, lat2, lng2):
#return distance as meter if you want km distance, remove "* 1000"
radius = 6371 * 1000
dLat = (lat2-lat1) * math.pi / 180
dLng = (lng2-lng1) * math.pi / 180
lat1 = lat1 * math.pi / 180
lat2 = lat2 * math.pi / 180
val = sin(dLat/2) * sin(dLat/2) + sin(dLng/2) * sin(dLng/2) * cos(lat1) * cos(lat2)
ang = 2 * atan2(sqrt(val), sqrt(1-val))
return radius * ang

Related

Calculating distance from two gps coordinates [duplicate]

I tried implementing the formula in Finding distances based on Latitude and Longitude. The applet does good for the two points I am testing:
Yet my code is not working.
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
It returns the distance 5447.05546147. Why?
The Vincenty distance is now deprecated since GeoPy version 1.13 - you should use geopy.distance.distance() instead!
The answers above are based on the haversine formula, which assumes the earth is a sphere, which results in errors of up to about 0.5% (according to help(geopy.distance)). The Vincenty distance uses more accurate ellipsoidal models, such as WGS-84, and is implemented in geopy. For example,
import geopy.distance
coords_1 = (52.2296756, 21.0122287)
coords_2 = (52.406374, 16.9251681)
print geopy.distance.geodesic(coords_1, coords_2).km
will print the distance of 279.352901604 kilometers using the default ellipsoid WGS-84. (You can also choose .miles or one of several other distance units.)
Just as a note, if you just need a quick and easy way of finding the distance between two points, I strongly recommend using the approach described in Kurt's answer below instead of reimplementing Haversine—see his post for rationale.
This answer focuses just on answering the specific bug the OP ran into.
It's because in Python, all the trigonometry functions use radians, not degrees.
You can either convert the numbers manually to radians, or use the radians function from the math module:
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")
The distance is now returning the correct value of 278.545589351 km.
For people (like me) coming here via a search engine, and who are just looking for a solution which works out of the box, I recommend installing mpu. Install it via pip install mpu --user and use it like this to get the haversine distance:
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.
An alternative package is gpxpy.
If you don't want dependencies, you can use:
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()
The other alternative package is haversine:
from haversine import haversine, Unit
lyon = (45.7597, 4.8422) # (latitude, longitude)
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
They claim to have performance optimization for distances between all points in two vectors:
from haversine import haversine_vector, Unit
lyon = (45.7597, 4.8422) # (latitude, longitude)
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])
I arrived at a much simpler and robust solution which is using geodesic from geopy package since you'll be highly likely using it in your project anyways so no extra package installation needed.
Here is my solution:
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
There are multiple ways to calculate the distance based on the coordinates i.e latitude and longitude
Install and import
from geopy import distance
from math import sin, cos, sqrt, atan2, radians
from sklearn.neighbors import DistanceMetric
import osrm
import numpy as np
Define coordinates
lat1, lon1, lat2, lon2, R = 20.9467,72.9520, 21.1702, 72.8311, 6373.0
coordinates_from = [lat1, lon1]
coordinates_to = [lat2, lon2]
Using 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)
Using haversine with 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))
Using 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)
Using 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)
Output
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)
You can use Uber's H3,point_dist() function to compute the spherical distance between two (latitude, longitude) points. We can set the return units ('km', 'm', or 'rads'). The default unit is km.
Example:
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
In the year 2022, one can post mixed JavaScript and Python code that solves this problem using more recent Python library, namely, geographiclib. The general benefit is that the users can run and see the result on the web page that runs on modern devices.
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.21.0/full/pyodide.js"></script>
(Year 2022, live JavaScript version.) Here is the code that solves this problem using a more recent JavaScript library. The general benefit is that the users can run and see the result on the web page that runs on modern devices.
// Using the 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` computation
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>
The simplest way is with the haversine package.
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')
Another interesting use of mixed JavaScript and Python through a Pyodide and WebAssembly implementation to obtain the solution using Python's libraries Pandas and geographiclib is also feasible.
I made extra effort using Pandas to prep the input data and when output was available, appended them to the solution column. Pandas provides many useful features for input/output for common needs. Its method toHtml is handy to present the final solution on the web page.
I found that the execution of the code in this answer is not successful on some iPhone and iPad devices. But on newer midrange Android devices it will run fine.
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 the 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.21.0/full/pyodide.js"></script>
Pyodide implementation<br>

How do I get distance in python or c#?

I want to be able to calculate the distance between two locations. So I have bill, his longitude and latitude is 5,6. And I have bob, his longitude and latitude is 9,2. Is there anything in c# or python that will make me able to get the distance in miles?
Distance between any 2 points on the surface shape (Earth) is calculated using the Haversine formula
1-First, convert the latitude and longitude values from decimal degrees to radians by dividing the values of longitude and latitude of both the points by 180/pi (pi=22/7)
Value of Latitude in Radians = lat / (180/pi)
Value of Longitude in Radians = long / (180/pi)
2- after converting lat, long to Radians for the 2 points
you should have now
(lat1_r,long1_r) ,(lat2_r,long2_r)
3-Calculate distance now by
d = 3963.0 * arccos[(sin(lat1_r) * sin(lat2_r)) + cos(lat1_r) * cos(lat2_r) * cos(long2_r– long1_r)]
4- this distance will be in miles, to get it in km
d in kilometers = 1.609344 * d in miles
5- write the logic in any language you like
Easy Haversine formula with code in Python
1-after converting lat,long to Radians for the 2 points
you should now have
(lat1_r,long1_r) ,(lat2_r,long2_r)
2- we need to get a,c, d using following formulas
`where φ is latitude (in radians), λ is longitude (in radians) , R is earth’s radius (mean radius = 6,371km);
Haversine formula Code in Python
from math import radians, cos, sin, asin, sqrt ,atan2
def distance(lat1, lat2, lon1, lon2):
# The math module contains a function named
# radians which converts from degrees to radians.
lon1 = radians(lon1)
lon2 = radians(lon2)
lat1 = radians(lat1)
lat2 = radians(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))
# you can use atan2 instead of asin and get same result
# c = 2 * atan2(sqrt(a), sqrt(1-a))
# Radius of earth in kilometers. Use 3956 for miles
r = 6371
# calculate the result
return(c * r)
if you believe the earth is flat then you can use euclidean distance :D
x is latitude and y is longitude
(x1,y1) the first point (x2,y2) is the second point, so you can build your own method in any language
d=√((x2 – x1)² + (y2 – y1)²).
References
https://www.geeksforgeeks.org/program-distance-two-points-earth/#:~:text=For%20this%20divide%20the%20values,is%20the%20radius%20of%20Earth.
https://www.youtube.com/watch?v=nsVsdHeTXIE&ab_channel=Qarbyte

Returning the lat long with minimum distance in python

I want to find the lat, long combination with minimum distance. x_lat, x_long are constant. I want to get combinations of y_latitude, y_longitude and calculate the distance and find out the minimum distance and return the corresponding y_latitude, y_longitude.
The following is trying,
x_lat = 33.50194395
x_long = -112.048885
y_latitude = ['56.16', '33.211045400000003', '37.36']
y_longitude = ['-117.3700631', '-118.244']
I have a distance function which would return the distance,
from math import radians, cos, sin, asin, sqrt
def distance(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))
km = 6367 * c
return km
So I tried something like the following,
dist = []
for i in itertools.product(y_latitude , y_longitude):
print i
dist.append(distance(float(i[1]),float(i[0]),float(x_long), float(x_lat)))
print dist.index(min(dist))
So this creates all possible combinations of y_latitude and y_longitude and calculates distance and returns the index of minimum distance. I am not able to make it return the corresponding y_latitude and y_longitude.
Here the index of minimum distance is 2 and output is 2. The required output is ('33.211045400000003', '-117.3700631'), which I am not able to make it return.
Can anybody help me in solving the last piece?
Thanks
Try this,
dist = []
for i in itertools.product(y_latitude , y_longitude):
dist.append([distance(float(i[1]),float(i[0]),float(x_long), float(x_lat)),i])
min_lat,min_lng = min(dist, key = lambda x: x[0])[1]
Append the lat and long along with the dist, And get min of first index,

Getting distance between two points based on latitude/longitude

I tried implementing the formula in Finding distances based on Latitude and Longitude. The applet does good for the two points I am testing:
Yet my code is not working.
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
It returns the distance 5447.05546147. Why?
The Vincenty distance is now deprecated since GeoPy version 1.13 - you should use geopy.distance.distance() instead!
The answers above are based on the haversine formula, which assumes the earth is a sphere, which results in errors of up to about 0.5% (according to help(geopy.distance)). The Vincenty distance uses more accurate ellipsoidal models, such as WGS-84, and is implemented in geopy. For example,
import geopy.distance
coords_1 = (52.2296756, 21.0122287)
coords_2 = (52.406374, 16.9251681)
print geopy.distance.geodesic(coords_1, coords_2).km
will print the distance of 279.352901604 kilometers using the default ellipsoid WGS-84. (You can also choose .miles or one of several other distance units.)
Just as a note, if you just need a quick and easy way of finding the distance between two points, I strongly recommend using the approach described in Kurt's answer below instead of reimplementing Haversine—see his post for rationale.
This answer focuses just on answering the specific bug the OP ran into.
It's because in Python, all the trigonometry functions use radians, not degrees.
You can either convert the numbers manually to radians, or use the radians function from the math module:
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")
The distance is now returning the correct value of 278.545589351 km.
For people (like me) coming here via a search engine, and who are just looking for a solution which works out of the box, I recommend installing mpu. Install it via pip install mpu --user and use it like this to get the haversine distance:
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.
An alternative package is gpxpy.
If you don't want dependencies, you can use:
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()
The other alternative package is haversine:
from haversine import haversine, Unit
lyon = (45.7597, 4.8422) # (latitude, longitude)
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
They claim to have performance optimization for distances between all points in two vectors:
from haversine import haversine_vector, Unit
lyon = (45.7597, 4.8422) # (latitude, longitude)
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])
I arrived at a much simpler and robust solution which is using geodesic from geopy package since you'll be highly likely using it in your project anyways so no extra package installation needed.
Here is my solution:
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
There are multiple ways to calculate the distance based on the coordinates i.e latitude and longitude
Install and import
from geopy import distance
from math import sin, cos, sqrt, atan2, radians
from sklearn.neighbors import DistanceMetric
import osrm
import numpy as np
Define coordinates
lat1, lon1, lat2, lon2, R = 20.9467,72.9520, 21.1702, 72.8311, 6373.0
coordinates_from = [lat1, lon1]
coordinates_to = [lat2, lon2]
Using 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)
Using haversine with 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))
Using 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)
Using 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)
Output
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)
You can use Uber's H3,point_dist() function to compute the spherical distance between two (latitude, longitude) points. We can set the return units ('km', 'm', or 'rads'). The default unit is km.
Example:
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
In the year 2022, one can post mixed JavaScript and Python code that solves this problem using more recent Python library, namely, geographiclib. The general benefit is that the users can run and see the result on the web page that runs on modern devices.
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.21.0/full/pyodide.js"></script>
(Year 2022, live JavaScript version.) Here is the code that solves this problem using a more recent JavaScript library. The general benefit is that the users can run and see the result on the web page that runs on modern devices.
// Using the 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` computation
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>
The simplest way is with the haversine package.
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')
Another interesting use of mixed JavaScript and Python through a Pyodide and WebAssembly implementation to obtain the solution using Python's libraries Pandas and geographiclib is also feasible.
I made extra effort using Pandas to prep the input data and when output was available, appended them to the solution column. Pandas provides many useful features for input/output for common needs. Its method toHtml is handy to present the final solution on the web page.
I found that the execution of the code in this answer is not successful on some iPhone and iPad devices. But on newer midrange Android devices it will run fine.
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 the 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.21.0/full/pyodide.js"></script>
Pyodide implementation<br>

Python geocode filtering by distance

I need to filter geocodes for near-ness to a location. For example, I want to filter a list of restaurant geocodes to identify those restaurants within 10 miles of my current location.
Can someone point me to a function that will convert a distance into latitude & longitude deltas? For example:
class GeoCode(object):
"""Simple class to store geocode as lat, lng attributes."""
def __init__(self, lat=0, lng=0, tag=None):
self.lat = lat
self.lng = lng
self.tag = None
def distance_to_deltas(geocode, max_distance):
"""Given a geocode and a distance, provides dlat, dlng
such that
|geocode.lat - dlat| <= max_distance
|geocode.lng - dlng| <= max_distance
"""
# implementation
# uses inverse Haversine, or other function?
return dlat, dlng
Note: I am using the supremum norm for distance.
There seems not to have been a good Python implementation. Fortunately the SO "Related articles" sidebar is our friend. This SO article points to an excellent article that gives the maths and a Java implementation. The actual function that you require is rather short and is embedded in my Python code below. Tested to extent shown. Read warnings in comments.
from math import sin, cos, asin, sqrt, degrees, radians
Earth_radius_km = 6371.0
RADIUS = Earth_radius_km
def haversine(angle_radians):
return sin(angle_radians / 2.0) ** 2
def inverse_haversine(h):
return 2 * asin(sqrt(h)) # radians
def distance_between_points(lat1, lon1, lat2, lon2):
# all args are in degrees
# WARNING: loss of absolute precision when points are near-antipodal
lat1 = radians(lat1)
lat2 = radians(lat2)
dlat = lat2 - lat1
dlon = radians(lon2 - lon1)
h = haversine(dlat) + cos(lat1) * cos(lat2) * haversine(dlon)
return RADIUS * inverse_haversine(h)
def bounding_box(lat, lon, distance):
# Input and output lats/longs are in degrees.
# Distance arg must be in same units as RADIUS.
# Returns (dlat, dlon) such that
# no points outside lat +/- dlat or outside lon +/- dlon
# are <= "distance" from the (lat, lon) point.
# Derived from: http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates
# WARNING: problems if North/South Pole is in circle of interest
# WARNING: problems if longitude meridian +/-180 degrees intersects circle of interest
# See quoted article for how to detect and overcome the above problems.
# Note: the result is independent of the longitude of the central point, so the
# "lon" arg is not used.
dlat = distance / RADIUS
dlon = asin(sin(dlat) / cos(radians(lat)))
return degrees(dlat), degrees(dlon)
if __name__ == "__main__":
# Examples from Jan Matuschek's article
def test(lat, lon, dist):
print "test bounding box", lat, lon, dist
dlat, dlon = bounding_box(lat, lon, dist)
print "dlat, dlon degrees", dlat, dlon
print "lat min/max rads", map(radians, (lat - dlat, lat + dlat))
print "lon min/max rads", map(radians, (lon - dlon, lon + dlon))
print "liberty to eiffel"
print distance_between_points(40.6892, -74.0444, 48.8583, 2.2945) # about 5837 km
print
print "calc min/max lat/lon"
degs = map(degrees, (1.3963, -0.6981))
test(*degs, dist=1000)
print
degs = map(degrees, (1.3963, -0.6981, 1.4618, -1.6021))
print degs, "distance", distance_between_points(*degs) # 872 km
This is how you calculate distances between lat/long pairs using the haversine formula:
import math
R = 6371 # km
dLat = (lat2-lat1) # Make sure it's in radians, not degrees
dLon = (lon2-lon1) # Idem
a = math.sin(dLat/2) * math.sin(dLat/2) +
math.cos(lat1) * math.cos(lat2) *
math.sin(dLon/2) * math.sin(dLon/2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
d = R * c;
It is now trivial to test "d" (also in km) against your threshold. If you want something else than km, adjust the radius.
I'm sorry I can't give you a drop-in solution, but I do not understand your code skeleton (see comment).
Also note that these days you probably want to use the spherical law of cosines rather than Haversine. The advantages in numerical stability are no longer worth it, and it's a hell of a lot simple to understand, code and use.
If you store data in MongoDB, it does nicely indexed geolocation searches for you, and is superior to the pure-Python solutions above because it will handle optimization for you.
http://www.mongodb.org/display/DOCS/Geospatial+Indexing
John Machin's answer helped me much. There is just a small mistake: latitudes and longitudes are swapped in boundigbox:
dlon = distance / RADIUS
dlat = asin(sin(dlon) / cos(radians(lon)))
return degrees(dlat), degrees(dlon)
this solves the problem. The reason is that longitudes don't changes their distance per degree - but latitudes do. Their distance is depending on the longitude.

Categories

Resources