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): |
||
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): |
||
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'): |
||
0 ignored issues
–
show
Unused Code
Coding Style
introduced
by
Loading history...
|
|||
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): |
||
0 ignored issues
–
show
|
|||
133 | return False |
||
134 | |||
135 | if('prefer_english_names' not in config['MapQuest']): |
||
0 ignored issues
–
show
|
|||
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): |
||
144 | return lookup_place_name_default |
||
145 | |||
146 | # Convert lat/lon to floats |
||
147 | if(not isinstance(lat, float)): |
||
148 | lat = float(lat) |
||
149 | if(not isinstance(lon, float)): |
||
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): |
||
164 | address = geolocation_info['address'] |
||
165 | for loc in ['city', 'state', 'country']: |
||
166 | if(loc in address): |
||
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): |
||
171 | lookup_place_name['default'] = address[loc] |
||
172 | |||
173 | if(lookup_place_name): |
||
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): |
||
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): |
||
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): |
||
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): |
||
0 ignored issues
–
show
|
|||
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): |
||
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): |
||
236 | return None |
||
237 | |||
238 | return result |
||
239 |