Passed
Push — master ( 9e03c4...3276cc )
by Jaisen
02:00
created

elodie/geolocation.py (14 issues)

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
standard_library.install_aliases()  # noqa
8
9
from os import path
10
11
import requests
12
import urllib.request
13
import urllib.parse
14
import urllib.error
15
16
from elodie.config import load_config
17
from elodie import constants
18
from elodie import log
19
from elodie.localstorage import Db
20
21
__KEY__ = None
22
__DEFAULT_LOCATION__ = 'Unknown Location'
23
__PREFER_ENGLISH_NAMES__ = None
24
25
26
def coordinates_by_name(name):
27
    # Try to get cached location first
28
    db = Db()
29
    cached_coordinates = db.get_location_coordinates(name)
30
    if(cached_coordinates is not None):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
31
        return {
32
            'latitude': cached_coordinates[0],
33
            'longitude': cached_coordinates[1]
34
        }
35
36
    # If the name is not cached then we go ahead with an API lookup
37
    geolocation_info = lookup(location=name)
38
39
    if(geolocation_info is not None):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
40
        if(
41
            'results' in geolocation_info and
42
            len(geolocation_info['results']) != 0 and
43
            'locations' in geolocation_info['results'][0] and
44
            len(geolocation_info['results'][0]['locations']) != 0
45
        ):
46
47
            # By default we use the first entry unless we find one with
48
            #   geocodeQuality=city.
49
            geolocation_result = geolocation_info['results'][0]
50
            use_location = geolocation_result['locations'][0]['latLng']
51
            # Loop over the locations to see if we come accross a
52
            #   geocodeQuality=city.
53
            # If we find a city we set that to the use_location and break
54
            for location in geolocation_result['locations']:
55
                if(
56
                    'latLng' in location and
57
                    'lat' in location['latLng'] and
58
                    'lng' in location['latLng'] and
59
                    location['geocodeQuality'].lower() == 'city'
60
                ):
61
                    use_location = location['latLng']
62
                    break
63
64
            return {
65
                'latitude': use_location['lat'],
66
                'longitude': use_location['lng']
67
            }
68
69
    return None
70
71
72
def decimal_to_dms(decimal):
73
    decimal = float(decimal)
74
    decimal_abs = abs(decimal)
75
    minutes, seconds = divmod(decimal_abs*3600, 60)
76
    degrees, minutes = divmod(minutes, 60)
77
    degrees = degrees
78
    sign = 1 if decimal >= 0 else -1
79
    return (degrees, minutes, seconds, sign)
80
81
82
def dms_to_decimal(degrees, minutes, seconds, direction=' '):
83
    sign = 1
84
    if(direction[0] in 'WSws'):
85
        sign = -1
86
    return (
87
        float(degrees) + old_div(float(minutes), 60) +
88
        old_div(float(seconds), 3600)
89
    ) * sign
90
91
92
def dms_string(decimal, type='latitude'):
93
    # Example string -> 38 deg 14' 27.82" S
94
    dms = decimal_to_dms(decimal)
95
    if type == 'latitude':
96
        direction = 'N' if decimal >= 0 else 'S'
97
    elif type == 'longitude':
98
        direction = 'E' if decimal >= 0 else 'W'
99
    return '{} deg {}\' {}" {}'.format(dms[0], dms[1], dms[2], direction)
100
101
102
def get_key():
103
    global __KEY__
104
    if __KEY__ is not None:
105
        return __KEY__
106
107
    if constants.mapquest_key is not None:
108
        __KEY__ = constants.mapquest_key
109
        return __KEY__
110
111
    config_file = '%s/config.ini' % constants.application_directory
112
    if not path.exists(config_file):
113
        return None
114
115
    config = load_config()
116
    if('MapQuest' not in config):
117
        return None
118
119
    __KEY__ = config['MapQuest']['key']
120
    return __KEY__
121
122
def get_prefer_english_names():
123
    global __PREFER_ENGLISH_NAMES__
124
    if __PREFER_ENGLISH_NAMES__ is not None:
125
        return __PREFER_ENGLISH_NAMES__
126
127
    config_file = '%s/config.ini' % constants.application_directory
128
    if not path.exists(config_file):
129
        return False
130
131
    config = load_config()
132
    if('MapQuest' not in config):
133
        return False
134
135
    if('prefer_english_names' not in config['MapQuest']):
136
        return False
137
138
    __PREFER_ENGLISH_NAMES__ = bool(config['MapQuest']['prefer_english_names'])
139
    return __PREFER_ENGLISH_NAMES__
140
141
def place_name(lat, lon):
142
    lookup_place_name_default = {'default': __DEFAULT_LOCATION__}
143
    if(lat is None or lon is None):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
144
        return lookup_place_name_default
145
146
    # Convert lat/lon to floats
147
    if(not isinstance(lat, float)):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
148
        lat = float(lat)
149
    if(not isinstance(lon, float)):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
150
        lon = float(lon)
151
152
    # Try to get cached location first
153
    db = Db()
154
    # 3km distace radious for a match
155
    cached_place_name = db.get_location_name(lat, lon, 3000)
156
    # We check that it's a dict to coerce an upgrade of the location
157
    #  db from a string location to a dictionary. See gh-160.
158
    if(isinstance(cached_place_name, dict)):
159
        return cached_place_name
160
161
    lookup_place_name = {}
162
    geolocation_info = lookup(lat=lat, lon=lon)
163
    if(geolocation_info is not None and 'address' in geolocation_info):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
164
        address = geolocation_info['address']
165
        for loc in ['city', 'state', 'country']:
166
            if(loc in address):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
167
                lookup_place_name[loc] = address[loc]
168
                # In many cases the desired key is not available so we
169
                #  set the most specific as the default.
170
                if('default' not in lookup_place_name):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
171
                    lookup_place_name['default'] = address[loc]
172
173
    if(lookup_place_name):
0 ignored issues
show
Unused Code Coding Style introduced by Ove Stavnås
There is an unnecessary parenthesis after if.
Loading history...
174
        db.add_location(lat, lon, lookup_place_name)
175
        # TODO: Maybe this should only be done on exit and not for every write.
176
        db.update_location_db()
177
178
    if('default' not in lookup_place_name):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
179
        lookup_place_name = lookup_place_name_default
180
181
    return lookup_place_name
182
183
184
def lookup(**kwargs):
185
    if(
186
        'location' not in kwargs and
187
        'lat' not in kwargs and
188
        'lon' not in kwargs
189
    ):
190
        return None
191
192
    key = get_key()
193
    prefer_english_names = get_prefer_english_names()
194
195
    if(key is None):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
196
        return None
197
198
    try:
199
        params = {'format': 'json', 'key': key}
200
        params.update(kwargs)
201
        path = '/geocoding/v1/address'
202
        if('lat' in kwargs and 'lon' in kwargs):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
203
            path = '/nominatim/v1/reverse.php'
204
        url = '%s%s?%s' % (
205
                    constants.mapquest_base_url,
206
                    path,
207
                    urllib.parse.urlencode(params)
208
              )
209
        headers = {}
210
        if(prefer_english_names):
211
            headers = {'Accept-Language':'en-EN,en;q=0.8'}
212
        r = requests.get(url, headers=headers)
213
        return parse_result(r.json())
214
    except requests.exceptions.RequestException as e:
215
        log.error(e)
216
        return None
217
    except ValueError as e:
218
        log.error(r.text)
219
        log.error(e)
220
        return None
221
222
223
def parse_result(result):
224
    if('error' in result):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
225
        return None
226
227
    if(
228
        'results' in result and
229
        len(result['results']) > 0 and
230
        'locations' in result['results'][0]
231
        and len(result['results'][0]['locations']) > 0 and
232
        'latLng' in result['results'][0]['locations'][0]
233
    ):
234
        latLng = result['results'][0]['locations'][0]['latLng']
235
        if(latLng['lat'] == 39.78373 and latLng['lng'] == -100.445882):
0 ignored issues
show
Unused Code Coding Style introduced by Jaisen Mathai
There is an unnecessary parenthesis after if.
Loading history...
236
            return None
237
238
    return result
239