2016-01-08 23:49:06 +01:00
|
|
|
"""Look up geolocation information for media objects."""
|
|
|
|
|
2015-10-08 11:21:31 +02:00
|
|
|
from os import path
|
|
|
|
from ConfigParser import ConfigParser
|
2015-10-14 05:26:55 +02:00
|
|
|
import fractions
|
|
|
|
|
2015-10-08 11:21:31 +02:00
|
|
|
import requests
|
2015-10-14 05:26:55 +02:00
|
|
|
import urllib
|
2015-10-08 11:21:31 +02:00
|
|
|
|
2015-10-28 08:19:21 +01:00
|
|
|
from elodie import constants
|
2015-12-24 20:39:28 +01:00
|
|
|
from elodie.localstorage import Db
|
2015-10-28 08:19:21 +01:00
|
|
|
|
2016-01-02 08:23:06 +01:00
|
|
|
|
2015-10-14 05:26:55 +02:00
|
|
|
class Fraction(fractions.Fraction):
|
2016-01-08 23:49:06 +01:00
|
|
|
|
2015-10-14 05:26:55 +02:00
|
|
|
"""Only create Fractions from floats.
|
2016-01-08 23:49:06 +01:00
|
|
|
|
|
|
|
Should be compatible with Python 2.6, though untested.
|
|
|
|
|
2015-10-14 05:26:55 +02:00
|
|
|
>>> Fraction(0.3)
|
|
|
|
Fraction(3, 10)
|
|
|
|
>>> Fraction(1.1)
|
|
|
|
Fraction(11, 10)
|
|
|
|
"""
|
2016-01-08 23:49:06 +01:00
|
|
|
|
2015-10-14 05:26:55 +02:00
|
|
|
def __new__(cls, value, ignore=None):
|
|
|
|
return fractions.Fraction.from_float(value).limit_denominator(99999)
|
|
|
|
|
2016-01-02 08:23:06 +01:00
|
|
|
|
2015-10-14 05:26:55 +02:00
|
|
|
def coordinates_by_name(name):
|
2015-12-29 09:07:50 +01:00
|
|
|
# Try to get cached location first
|
|
|
|
db = Db()
|
|
|
|
cached_coordinates = db.get_location_coordinates(name)
|
|
|
|
if(cached_coordinates is not None):
|
|
|
|
return {
|
2016-01-02 18:34:43 +01:00
|
|
|
'latitude': cached_coordinates[0],
|
|
|
|
'longitude': cached_coordinates[1]
|
2015-12-29 09:07:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
# If the name is not cached then we go ahead with an API lookup
|
2015-10-14 05:26:55 +02:00
|
|
|
geolocation_info = lookup(name)
|
|
|
|
|
|
|
|
if(geolocation_info is not None):
|
2016-01-02 08:23:06 +01:00
|
|
|
if(
|
|
|
|
'results' in geolocation_info and
|
|
|
|
len(geolocation_info['results']) != 0 and
|
|
|
|
'locations' in geolocation_info['results'][0] and
|
|
|
|
len(geolocation_info['results'][0]['locations']) != 0
|
|
|
|
):
|
|
|
|
|
|
|
|
# By default we use the first entry unless we find one with
|
|
|
|
# geocodeQuality=city.
|
|
|
|
geolocation_result = geolocation_info['results'][0]
|
|
|
|
use_location = geolocation_result['locations'][0]['latLng']
|
|
|
|
# Loop over the locations to see if we come accross a
|
|
|
|
# geocodeQuality=city.
|
2015-10-14 05:26:55 +02:00
|
|
|
# If we find a city we set that to the use_location and break
|
2016-01-02 08:23:06 +01:00
|
|
|
for location in geolocation_result['locations']:
|
|
|
|
if(
|
|
|
|
'latLng' in location and
|
|
|
|
'lat' in location['latLng'] and
|
|
|
|
'lng' in location['latLng'] and
|
|
|
|
location['geocodeQuality'].lower() == 'city'
|
|
|
|
):
|
2015-10-14 05:26:55 +02:00
|
|
|
use_location = location['latLng']
|
|
|
|
break
|
2016-01-02 08:23:06 +01:00
|
|
|
|
2015-10-14 05:26:55 +02:00
|
|
|
return {
|
2016-01-02 18:34:43 +01:00
|
|
|
'latitude': use_location['lat'],
|
|
|
|
'longitude': use_location['lng']
|
2015-10-14 05:26:55 +02:00
|
|
|
}
|
2016-01-02 08:23:06 +01:00
|
|
|
|
2015-10-14 05:26:55 +02:00
|
|
|
return None
|
|
|
|
|
2016-01-02 08:23:06 +01:00
|
|
|
|
2016-06-21 20:19:40 +02:00
|
|
|
def decimal_to_dms(decimal):
|
|
|
|
decimal = float(decimal)
|
|
|
|
decimal_abs = abs(decimal)
|
|
|
|
minutes,seconds = divmod(decimal_abs*3600,60)
|
|
|
|
degrees,minutes = divmod(minutes,60)
|
|
|
|
degrees = degrees
|
|
|
|
sign = 1 if decimal >= 0 else -1
|
|
|
|
return (degrees,minutes,seconds, sign)
|
2015-10-14 05:26:55 +02:00
|
|
|
|
2016-01-02 08:23:06 +01:00
|
|
|
|
|
|
|
def dms_to_decimal(degrees, minutes, seconds, direction=' '):
|
|
|
|
sign = 1
|
2016-02-12 20:22:26 +01:00
|
|
|
if(direction[0] in 'WSws'):
|
2016-01-02 08:23:06 +01:00
|
|
|
sign = -1
|
|
|
|
return (
|
|
|
|
float(degrees) + float(minutes) / 60 + float(seconds) / 3600
|
|
|
|
) * sign
|
|
|
|
|
|
|
|
|
2016-06-21 20:19:40 +02:00
|
|
|
def dms_string(decimal, type='latitude'):
|
|
|
|
# Example string -> 38 deg 14' 27.82" S
|
|
|
|
dms = decimal_to_dms(decimal)
|
|
|
|
if type == 'latitude':
|
|
|
|
direction = 'N' if decimal >= 0 else 'S'
|
|
|
|
elif type == 'longitude':
|
|
|
|
direction = 'E' if decimal >= 0 else 'W'
|
|
|
|
return '{} deg {}\' {}" {}'.format(dms[0], dms[1], dms[2], direction)
|
|
|
|
|
|
|
|
|
2015-10-14 05:26:55 +02:00
|
|
|
def get_key():
|
2015-11-19 11:31:32 +01:00
|
|
|
config_file = '%s/config.ini' % constants.application_directory
|
2015-10-13 04:40:20 +02:00
|
|
|
if not path.exists(config_file):
|
2015-10-08 11:21:31 +02:00
|
|
|
return None
|
2016-01-02 08:23:06 +01:00
|
|
|
|
2015-10-08 11:21:31 +02:00
|
|
|
config = ConfigParser()
|
2015-10-13 04:40:20 +02:00
|
|
|
config.read(config_file)
|
2015-10-08 11:21:31 +02:00
|
|
|
if('MapQuest' not in config.sections()):
|
|
|
|
return None
|
|
|
|
|
2015-10-14 05:26:55 +02:00
|
|
|
return config.get('MapQuest', 'key')
|
2015-10-08 11:21:31 +02:00
|
|
|
|
2016-01-02 08:23:06 +01:00
|
|
|
|
2015-10-08 11:21:31 +02:00
|
|
|
def place_name(lat, lon):
|
2016-04-21 07:23:57 +02:00
|
|
|
# Convert lat/lon to floats
|
|
|
|
if not isinstance(lat, float):
|
|
|
|
lat = float(lat)
|
|
|
|
if not isinstance(lon, float):
|
|
|
|
lon = float(lon)
|
2015-12-24 20:39:28 +01:00
|
|
|
|
|
|
|
# Try to get cached location first
|
|
|
|
db = Db()
|
|
|
|
# 3km distace radious for a match
|
2016-01-02 08:23:06 +01:00
|
|
|
cached_place_name = db.get_location_name(lat, lon, 3000)
|
2015-12-24 20:39:28 +01:00
|
|
|
if(cached_place_name is not None):
|
|
|
|
return cached_place_name
|
|
|
|
|
2016-01-02 08:23:06 +01:00
|
|
|
lookup_place_name = None
|
2015-10-08 11:21:31 +02:00
|
|
|
geolocation_info = reverse_lookup(lat, lon)
|
|
|
|
if(geolocation_info is not None):
|
|
|
|
if('address' in geolocation_info):
|
|
|
|
address = geolocation_info['address']
|
|
|
|
if('city' in address):
|
2015-12-24 20:39:28 +01:00
|
|
|
lookup_place_name = address['city']
|
2015-10-08 11:21:31 +02:00
|
|
|
elif('state' in address):
|
2015-12-24 20:39:28 +01:00
|
|
|
lookup_place_name = address['state']
|
2015-10-08 11:21:31 +02:00
|
|
|
elif('country' in address):
|
2015-12-24 20:39:28 +01:00
|
|
|
lookup_place_name = address['country']
|
|
|
|
|
|
|
|
if(lookup_place_name is not None):
|
|
|
|
db.add_location(lat, lon, lookup_place_name)
|
|
|
|
# TODO: Maybe this should only be done on exit and not for every write.
|
|
|
|
db.update_location_db()
|
|
|
|
return lookup_place_name
|
2015-10-14 05:26:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
def reverse_lookup(lat, lon):
|
|
|
|
if(lat is None or lon is None):
|
|
|
|
return None
|
|
|
|
|
|
|
|
key = get_key()
|
|
|
|
|
|
|
|
try:
|
|
|
|
params = {'format': 'json', 'key': key, 'lat': lat, 'lon': lon}
|
2016-01-28 19:30:13 +01:00
|
|
|
headers = {"Accept-Language": constants.accepted_language}
|
2016-01-02 08:23:06 +01:00
|
|
|
r = requests.get(
|
|
|
|
'http://open.mapquestapi.com/nominatim/v1/reverse.php?%s' %
|
2016-02-12 00:24:42 +01:00
|
|
|
urllib.urlencode(params), headers=headers
|
2016-01-02 08:23:06 +01:00
|
|
|
)
|
2015-10-14 05:26:55 +02:00
|
|
|
return r.json()
|
|
|
|
except requests.exceptions.RequestException as e:
|
2016-01-02 08:23:06 +01:00
|
|
|
if(constants.debug is True):
|
2015-10-28 08:19:21 +01:00
|
|
|
print e
|
2015-10-14 05:26:55 +02:00
|
|
|
return None
|
|
|
|
except ValueError as e:
|
2016-01-02 08:23:06 +01:00
|
|
|
if(constants.debug is True):
|
2015-10-28 08:19:21 +01:00
|
|
|
print r.text
|
|
|
|
print e
|
2015-10-14 05:26:55 +02:00
|
|
|
return None
|
|
|
|
|
2016-01-02 08:23:06 +01:00
|
|
|
|
2015-10-14 05:26:55 +02:00
|
|
|
def lookup(name):
|
|
|
|
if(name is None or len(name) == 0):
|
|
|
|
return None
|
|
|
|
|
|
|
|
key = get_key()
|
|
|
|
|
|
|
|
try:
|
|
|
|
params = {'format': 'json', 'key': key, 'location': name}
|
2016-01-02 08:23:06 +01:00
|
|
|
if(constants.debug is True):
|
|
|
|
print 'http://open.mapquestapi.com/geocoding/v1/address?%s' % urllib.urlencode(params) # noqa
|
|
|
|
r = requests.get(
|
|
|
|
'http://open.mapquestapi.com/geocoding/v1/address?%s' %
|
|
|
|
urllib.urlencode(params)
|
|
|
|
)
|
2015-10-14 05:26:55 +02:00
|
|
|
return r.json()
|
|
|
|
except requests.exceptions.RequestException as e:
|
2016-01-02 08:23:06 +01:00
|
|
|
if(constants.debug is True):
|
2015-10-28 08:19:21 +01:00
|
|
|
print e
|
2015-10-14 05:26:55 +02:00
|
|
|
return None
|
|
|
|
except ValueError as e:
|
2016-01-02 08:23:06 +01:00
|
|
|
if(constants.debug is True):
|
2015-10-28 08:19:21 +01:00
|
|
|
print r.text
|
|
|
|
print e
|
2015-10-14 05:26:55 +02:00
|
|
|
return None
|