lab_weather/geocoder/distance.py

96 lines
2.6 KiB
Python

#!/usr/bin/python
# coding: utf8
from __future__ import absolute_import
from math import radians, cos, sin, asin, sqrt
from geocoder.location import Location
AVG_EARTH_RADIUS = 6371 # in km
def Distance(*args, **kwargs):
total = 0.0
last = None
if len(args) == 1 and isinstance(args, (list, tuple)):
args = args[0]
if len(args) <= 1:
raise ValueError("Distance needs at least two locations")
for location in args:
if last:
distance = haversine(Location(last), Location(location), **kwargs)
if distance:
total += distance
last = location
return total
def haversine(point1, point2, **kwargs):
""" Calculate the great-circle distance bewteen two points on the Earth surface.
:input: two 2-tuples, containing the latitude and longitude of each point
in decimal degrees.
Example: haversine((45.7597, 4.8422), (48.8567, 2.3508))
:output: Returns the distance bewteen the two points.
The default unit is kilometers. Miles can be returned
if the ``miles`` parameter is set to True.
"""
lookup_units = {
'miles': 'miles',
'mile': 'miles',
'mi': 'miles',
'ml': 'miles',
'kilometers': 'kilometers',
'kilometres': 'kilometers',
'kilometer': 'kilometers',
'kilometre': 'kilometers',
'km': 'kilometers',
'meters': 'meters',
'metres': 'meters',
'meter': 'meters',
'metre': 'meters',
'm': 'meters',
'feet': 'feet',
'f': 'feet',
'ft': 'feet',
}
if point1.ok and point2.ok:
# convert all latitudes/longitudes from decimal degrees to radians
lat1, lng1, lat2, lng2 = list(map(radians, point1.latlng + point2.latlng))
# calculate haversine
lat = lat2 - lat1
lng = lng2 - lng1
d = sin(lat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(lng / 2) ** 2
h = 2 * AVG_EARTH_RADIUS * asin(sqrt(d))
# Measurements
units = kwargs.get('units', 'kilometers').lower()
units_calculation = {
'miles': h * 0.621371,
'feet': h * 0.621371 * 5280,
'meters': h * 1000,
'kilometers': h,
}
if units in lookup_units:
return units_calculation[lookup_units[units]]
else:
raise ValueError("Unknown units of measurement")
else:
print(u'[WARNING] Error calculating the following two locations.\n'
'Points: {0} to {1}'.format(point1.location, point2.location))
if __name__ == '__main__':
d = Distance('Ottawa, ON', 'Toronto, ON', 'Montreal, QC')
print(d)