Completed
Push — master ( df6d80...16f402 )
by Jaisen
10s
created

parse_result()   C

Complexity

Conditions 9

Duplication

Lines 0
Ratio 0 %

Size

Total Lines 16

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
c 1
b 0
f 0
dl 0
loc 16
rs 6.4615
1
"""Look up geolocation information for media objects."""
2
from __future__ import print_function
3
from __future__ import division
4
from future import standard_library
5
from past.utils import old_div
6
7
from os import path
8
from configparser import ConfigParser
9
10
standard_library.install_aliases()  # noqa
11
12
import requests
13
import urllib.request
14
import urllib.parse
15
import urllib.error
16
17
from elodie import constants
18
from elodie.localstorage import Db
19
20
__KEY__ = None
21
22
23
def coordinates_by_name(name):
24
    # Try to get cached location first
25
    db = Db()
26
    cached_coordinates = db.get_location_coordinates(name)
27
    if(cached_coordinates is not None):
28
        return {
29
            'latitude': cached_coordinates[0],
30
            'longitude': cached_coordinates[1]
31
        }
32
33
    # If the name is not cached then we go ahead with an API lookup
34
    geolocation_info = lookup(location=name)
35
36
    if(geolocation_info is not None):
37
        if(
38
            'results' in geolocation_info and
39
            len(geolocation_info['results']) != 0 and
40
            'locations' in geolocation_info['results'][0] and
41
            len(geolocation_info['results'][0]['locations']) != 0
42
        ):
43
44
            # By default we use the first entry unless we find one with
45
            #   geocodeQuality=city.
46
            geolocation_result = geolocation_info['results'][0]
47
            use_location = geolocation_result['locations'][0]['latLng']
48
            # Loop over the locations to see if we come accross a
49
            #   geocodeQuality=city.
50
            # If we find a city we set that to the use_location and break
51
            for location in geolocation_result['locations']:
52
                if(
53
                    'latLng' in location and
54
                    'lat' in location['latLng'] and
55
                    'lng' in location['latLng'] and
56
                    location['geocodeQuality'].lower() == 'city'
57
                ):
58
                    use_location = location['latLng']
59
                    break
60
61
            return {
62
                'latitude': use_location['lat'],
63
                'longitude': use_location['lng']
64
            }
65
66
    return None
67
68
69
def decimal_to_dms(decimal):
70
    decimal = float(decimal)
71
    decimal_abs = abs(decimal)
72
    minutes, seconds = divmod(decimal_abs*3600, 60)
73
    degrees, minutes = divmod(minutes, 60)
74
    degrees = degrees
75
    sign = 1 if decimal >= 0 else -1
76
    return (degrees, minutes, seconds, sign)
77
78
79
def dms_to_decimal(degrees, minutes, seconds, direction=' '):
80
    sign = 1
81
    if(direction[0] in 'WSws'):
82
        sign = -1
83
    return (
84
        float(degrees) + old_div(float(minutes), 60) +
85
        old_div(float(seconds), 3600)
86
    ) * sign
87
88
89
def dms_string(decimal, type='latitude'):
90
    # Example string -> 38 deg 14' 27.82" S
91
    dms = decimal_to_dms(decimal)
92
    if type == 'latitude':
93
        direction = 'N' if decimal >= 0 else 'S'
94
    elif type == 'longitude':
95
        direction = 'E' if decimal >= 0 else 'W'
96
    return '{} deg {}\' {}" {}'.format(dms[0], dms[1], dms[2], direction)
97
98
99
def get_key():
100
    global __KEY__
101
    if __KEY__ is not None:
102
        return __KEY__
103
104
    config_file = '%s/config.ini' % constants.application_directory
105
    if not path.exists(config_file):
106
        return None
107
108
    config = ConfigParser()
109
    config.read(config_file)
110
    if('MapQuest' not in config.sections()):
111
        return None
112
113
    __KEY__ = config.get('MapQuest', 'key')
114
    return __KEY__
115
116
117
def place_name(lat, lon):
118
    # Convert lat/lon to floats
119
    if not isinstance(lat, float):
120
        lat = float(lat)
121
    if not isinstance(lon, float):
122
        lon = float(lon)
123
124
    # Try to get cached location first
125
    db = Db()
126
    # 3km distace radious for a match
127
    cached_place_name = db.get_location_name(lat, lon, 3000)
128
    if(cached_place_name is not None):
129
        return cached_place_name
130
131
    lookup_place_name = None
132
    geolocation_info = lookup(lat=lat, lon=lon)
133
    if(geolocation_info is not None):
134
        if('address' in geolocation_info):
135
            address = geolocation_info['address']
136
            if('city' in address):
137
                lookup_place_name = address['city']
138
            elif('state' in address):
139
                lookup_place_name = address['state']
140
            elif('country' in address):
141
                lookup_place_name = address['country']
142
143
    if(lookup_place_name is not None):
144
        db.add_location(lat, lon, lookup_place_name)
145
        # TODO: Maybe this should only be done on exit and not for every write.
146
        db.update_location_db()
147
    return lookup_place_name
148
149
150
def lookup(**kwargs):
151
    if(
152
        'location' not in kwargs and
153
        'lat' not in kwargs and
154
        'lon' not in kwargs
155
    ):
156
        return None
157
158
    key = get_key()
159
160
    if(key is None):
161
        return None
162
163
    try:
164
        params = {'format': 'json', 'key': key}
165
        params.update(kwargs)
166
        path = '/geocoding/v1/address'
167
        if('lat' in kwargs and 'lon' in kwargs):
168
            path = '/nominatim/v1/reverse.php'
169
        url = 'http://open.mapquestapi.com%s?%s' % (
170
                    path,
171
                    urllib.parse.urlencode(params)
172
              )
173
        if(constants.debug is True):
174
            print(url)
175
        r = requests.get(url)
176
        return parse_result(r.json())
177
    except requests.exceptions.RequestException as e:
178
        if(constants.debug is True):
179
            print(e)
180
        return None
181
    except ValueError as e:
182
        if(constants.debug is True):
183
            print(r.text)
184
            print(e)
185
        return None
186
187
188
def parse_result(result):
189
    if('error' in result):
190
        return None
191
192
    if(
193
        'results' in result and
194
        len(result['results']) > 0 and
195
        'locations' in result['results'][0]
196
        and len(result['results'][0]['locations']) > 0 and
197
        'latLng' in result['results'][0]['locations'][0]
198
    ):
199
        latLng = result['results'][0]['locations'][0]['latLng']
200
        if(latLng['lat'] == 39.78373 and latLng['lng'] == -100.445882):
201
            return None
202
203
    return result
204