|
@@ -0,0 +1,628 @@
|
|
1
|
+#!/usr/bin/env python
|
|
2
|
+# -*- coding: utf-8 -*-
|
|
3
|
+#
|
|
4
|
+"""
|
|
5
|
+geo.osm (Open Streetmap)
|
|
6
|
+========================
|
|
7
|
+
|
|
8
|
+**Author:**
|
|
9
|
+
|
|
10
|
+* Dirk Alders <sudo-dirk@mount-mockery.de>
|
|
11
|
+
|
|
12
|
+**Description:**
|
|
13
|
+
|
|
14
|
+ This module is a submodule of :mod:`geo` and supports functions and classes for Open Streetmap.
|
|
15
|
+
|
|
16
|
+**Contentlist:**
|
|
17
|
+
|
|
18
|
+* :func:`geo.osm.landmark_link`
|
|
19
|
+
|
|
20
|
+**Unittest:**
|
|
21
|
+
|
|
22
|
+ See also the :download:`unittest <../../geo/_testresults_/unittest.pdf>` documentation.
|
|
23
|
+"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+MAP_STANDARD = 'N'
|
|
27
|
+"""MAP definition for Standard Map"""
|
|
28
|
+MAP_LOCAL_TRAFIC = 'TN'
|
|
29
|
+"""MAP definition for Local Trafic Map"""
|
|
30
|
+MAP_CYCLEMAP = 'CN'
|
|
31
|
+"""MAP definition for Cyclemap"""
|
|
32
|
+MAP_HUMANITARIAN = 'HN'
|
|
33
|
+"""MAP definition for Humanitarian Map"""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+def landmark_link(coord, zoom_level=13, map_code=MAP_STANDARD):
|
|
37
|
+ """
|
|
38
|
+ :param coord: Target coordinate.
|
|
39
|
+ :type coord: geo.gps.coordinate
|
|
40
|
+ :param zoom_level: The zoom level of the map (see https://wiki.openstreetmap.org/wiki/Zoom_levels for more information)
|
|
41
|
+ :type zoom_level: int
|
|
42
|
+ :param map_code: One of the map_codes :class:`MAP_STANDARD`, :class:`MAP_LOCAL_TRAFIC`, :class:`MAP_CYCLEMAP`, :class:`MAP_HUMANITARIAN`.
|
|
43
|
+ :type map_code: str
|
|
44
|
+ :return: An openstreetmap-url for marking a position in a map.
|
|
45
|
+ :rtype: str
|
|
46
|
+
|
|
47
|
+ This Method generates an openstreetmap-url for marking a position in a map.
|
|
48
|
+
|
|
49
|
+ .. code-block:: python
|
|
50
|
+
|
|
51
|
+ >>> import geo
|
|
52
|
+
|
|
53
|
+ >>> gb = geo.gps.coordinate(lat=53.6908298,lon=12.1583252)
|
|
54
|
+ >>> geo.osm.landmark_link(gb)
|
|
55
|
+ 'http://www.openstreetmap.org?mlat=53.690830&mlon=12.158325&zoom=13&layers=N'
|
|
56
|
+ """
|
|
57
|
+ lon = coord[coord.LONGITUDE]
|
|
58
|
+ lat = coord[coord.LATITUDE]
|
|
59
|
+ #
|
|
60
|
+ if lon is not None and lat is not None:
|
|
61
|
+ link = 'http://www.openstreetmap.org?mlat=%(' + coord.LATITUDE + ')f&mlon=%(' + coord.LONGITUDE + ')f&zoom=%(zoom)d&layers=%(map)s'
|
|
62
|
+ return link % {coord.LATITUDE: lat,
|
|
63
|
+ coord.LONGITUDE: lon,
|
|
64
|
+ 'zoom': zoom_level,
|
|
65
|
+ 'map': map_code}
|
|
66
|
+ else:
|
|
67
|
+ return None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+class map_spec(object):
|
|
71
|
+ def __init__(self, **kwargs):
|
|
72
|
+ coord1 = kwargs.get('coord1')
|
|
73
|
+ coord2 = kwargs.get('coord2')
|
|
74
|
+ zoom_level = kwargs.get('zoom_level')
|
|
75
|
+ x = kwargs.get('x')
|
|
76
|
+ y = kwargs.get('y')
|
|
77
|
+
|
|
78
|
+ def get_resolution(self):
|
|
79
|
+ pass
|
|
80
|
+
|
|
81
|
+ def get_coord_range(self):
|
|
82
|
+ pass
|
|
83
|
+
|
|
84
|
+ def get_map(self):
|
|
85
|
+ pass
|
|
86
|
+
|
|
87
|
+ def point_to_coord(self, xy):
|
|
88
|
+ pass
|
|
89
|
+
|
|
90
|
+ def coord_to_point(self, coord):
|
|
91
|
+ pass
|
|
92
|
+
|
|
93
|
+class osm_map(object):
|
|
94
|
+ def __init__(self, **kwargs):
|
|
95
|
+ self.__map_spec__ = map_spec(kwargs)
|
|
96
|
+ map_code = kwargs.get('map_code')
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+'''
|
|
100
|
+class get_from_web():
|
|
101
|
+ """
|
|
102
|
+ Class to download images from web.
|
|
103
|
+ """
|
|
104
|
+ def __init__(self, props=None):
|
|
105
|
+ """
|
|
106
|
+ Init routine for class get_from_web.
|
|
107
|
+ @param props: myapptools.AppProp instance with proxy information. This has to be a dictionary (see self._set_props).
|
|
108
|
+ """
|
|
109
|
+ self.props = props
|
|
110
|
+ if props != None:
|
|
111
|
+ # install a callback if property 'Proxy' had been changed. it will be called with the new proxy information.
|
|
112
|
+ props.InstallPostSetCallback('Proxy', self._props_callback)
|
|
113
|
+
|
|
114
|
+ def _props_callback(self, proxy):
|
|
115
|
+ """
|
|
116
|
+ Routione which is called, if proxy information had been changed. It will set the new proxy information.
|
|
117
|
+ @param proxy: dictionary with proxy information
|
|
118
|
+ """
|
|
119
|
+ self._set_props(**proxy)
|
|
120
|
+
|
|
121
|
+ def _set_props(self, use_proxy, host, port, use_user, user, use_passwd, passwd=None):
|
|
122
|
+ """
|
|
123
|
+ Routine to set the proxy information.
|
|
124
|
+ @param host: host to connect to
|
|
125
|
+ @param port: port which is used to connect the proxy
|
|
126
|
+ @param user: username for proxy
|
|
127
|
+ @param passwd: password to be used for user
|
|
128
|
+ """
|
|
129
|
+ proxy_str = None
|
|
130
|
+ if not use_proxy:
|
|
131
|
+ proxy_support = urllib2.ProxyHandler({})
|
|
132
|
+ opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)
|
|
133
|
+ urllib2.install_opener(opener)
|
|
134
|
+ elif not use_user:
|
|
135
|
+ proxy_str = "http://%s:%d" % (host, port)
|
|
136
|
+ elif not use_passwd or passwd == None:
|
|
137
|
+ proxy_str = "http://%s@%s:%d" % (user, host, port)
|
|
138
|
+ else:
|
|
139
|
+ proxy_str = "http://%s:%s@%s:%d" % (user, passwd, host, port)
|
|
140
|
+ if proxy_str != None:
|
|
141
|
+ proxy_support = urllib2.ProxyHandler({"http": proxy_str})
|
|
142
|
+ opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)
|
|
143
|
+ urllib2.install_opener(opener)
|
|
144
|
+
|
|
145
|
+ def get_image(self, url):
|
|
146
|
+ """
|
|
147
|
+ Routine to download an image from web.
|
|
148
|
+ @param url: url to the image
|
|
149
|
+ @return: image (type: python Image)
|
|
150
|
+ """
|
|
151
|
+ #try:
|
|
152
|
+ f = urllib2.urlopen(url)
|
|
153
|
+ im = Image.open(StringIO.StringIO(f.read()))
|
|
154
|
+ f.close()
|
|
155
|
+ return im
|
|
156
|
+ #except ???:
|
|
157
|
+ # print "exception in get_from_web().get_image('%s')" % url
|
|
158
|
+ # return None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+class tile_handler(dict):
|
|
162
|
+ """
|
|
163
|
+ Class to handle some tile_source classes and a default tile_source.
|
|
164
|
+ """
|
|
165
|
+ TILE_SIZE = 256
|
|
166
|
+
|
|
167
|
+ class tile_source():
|
|
168
|
+ """
|
|
169
|
+ Class to get tile by cache or url. It also stores the downloaded tiles to the cache directory.
|
|
170
|
+ """
|
|
171
|
+ TILEPATH = 'osm_tiles'
|
|
172
|
+
|
|
173
|
+ def __init__(self, gfw, name, url, min_zoom, max_zoom):
|
|
174
|
+ """
|
|
175
|
+ Init routine for class tile_source.
|
|
176
|
+ @param gfw: instance of get_from_web
|
|
177
|
+ @param name: name of tile_type
|
|
178
|
+ @param url: base url without zoom or tile information
|
|
179
|
+ @param min_zoom: minimum existing zoom level for this tile type
|
|
180
|
+ @param max_zoom: maximum existing zoom level for this tile type
|
|
181
|
+ """
|
|
182
|
+ self.gfw = gfw
|
|
183
|
+ self._name = name
|
|
184
|
+ self._url = url
|
|
185
|
+ self._zooms = range(min_zoom, max_zoom + 1)
|
|
186
|
+
|
|
187
|
+ def get_path(self, x, y, zoom_lvl):
|
|
188
|
+ """
|
|
189
|
+ Routine to get the tile-path information for a specific tile.
|
|
190
|
+ @param x: horizontal tile number
|
|
191
|
+ @param y: vertical tile number
|
|
192
|
+ @param zoom_lvl: zoom level for the tile
|
|
193
|
+ @return: path to tile as string
|
|
194
|
+ """
|
|
195
|
+ def intstr(num):
|
|
196
|
+ return str(int(num))
|
|
197
|
+ return os.path.join(__basepath__, self.TILEPATH, self._name, intstr(zoom_lvl), intstr(x), intstr(y) + '.png')
|
|
198
|
+
|
|
199
|
+ def get_url(self, x, y, zoom_lvl):
|
|
200
|
+ """
|
|
201
|
+ Routine to get the url information for a specific tile.
|
|
202
|
+ @param x: horizontal tile number
|
|
203
|
+ @param y: vertical tile number
|
|
204
|
+ @param zoom_lvl: zoom level for the tile
|
|
205
|
+ @return: url to tile as string
|
|
206
|
+ """
|
|
207
|
+ def intstr(num):
|
|
208
|
+ return str(int(num))
|
|
209
|
+ return self._url + '/' + intstr(zoom_lvl) + '/' + intstr(x) + '/' + intstr(y) + '.png'
|
|
210
|
+
|
|
211
|
+ def get_zooms(self):
|
|
212
|
+ """
|
|
213
|
+ Routine to get a list of available zoom levels for this source.
|
|
214
|
+ @return: zoom levels as a list (e.g. [0,1,2,3]).
|
|
215
|
+ """
|
|
216
|
+ return self._zooms
|
|
217
|
+
|
|
218
|
+ def get_tile(self, x, y, zoom_lvl, max_age, from_cache):
|
|
219
|
+ """
|
|
220
|
+ Routine to get a tile.
|
|
221
|
+ @param x: horizontal tile number
|
|
222
|
+ @param y: vertical tile number
|
|
223
|
+ @param zoom_lvl: zoom level for the tile
|
|
224
|
+ @param max_age: maximum age where no www-refresh is needed
|
|
225
|
+ @param from_cache: if True the tile is from cache
|
|
226
|
+ @return: tile as Image
|
|
227
|
+ """
|
|
228
|
+ filename = self.get_path(x, y, zoom_lvl)
|
|
229
|
+ url = self.get_url(x, y, zoom_lvl)
|
|
230
|
+ if from_cache:
|
|
231
|
+ try:
|
|
232
|
+ return Image.open(filename)
|
|
233
|
+ except:
|
|
234
|
+ return None
|
|
235
|
+ else:
|
|
236
|
+ local_time = calendar.timegm(time.gmtime())
|
|
237
|
+ try:
|
|
238
|
+ tile_tm = os.path.getmtime(filename)
|
|
239
|
+ except:
|
|
240
|
+ tile_tm = local_time - max_age - 1
|
|
241
|
+ if local_time - tile_tm > max_age: # age depending refresh.
|
|
242
|
+ im = self.gfw.get_image(url)
|
|
243
|
+ try:
|
|
244
|
+ self.save(im, filename)
|
|
245
|
+ except:
|
|
246
|
+ print "exception in tile_handler().get_tile"
|
|
247
|
+ #TODO: exception handling.
|
|
248
|
+ pass
|
|
249
|
+ return im
|
|
250
|
+ else:
|
|
251
|
+ return None
|
|
252
|
+
|
|
253
|
+ def save(self, im, filename):
|
|
254
|
+ """
|
|
255
|
+ Routine to save the image to cache (directory).
|
|
256
|
+ @param im: image to save (type: python Image)
|
|
257
|
+ @param filename: name of the file, which will be created
|
|
258
|
+ """
|
|
259
|
+ dirname = os.path.dirname(filename)
|
|
260
|
+ if not os.path.exists(dirname):
|
|
261
|
+ os.makedirs(dirname)
|
|
262
|
+ im.save(filename)
|
|
263
|
+
|
|
264
|
+ def __init__(self, gfw, props=None):
|
|
265
|
+ """
|
|
266
|
+ Init routine for class tile_handler
|
|
267
|
+ @param gfw: instance of get_from_web
|
|
268
|
+ @param props: myapptools.AppProp instance with tilehandler information. This has to be a dictionary (see self._set_proxy).
|
|
269
|
+ """
|
|
270
|
+ dict.__init__(self)
|
|
271
|
+ self.gfw = gfw
|
|
272
|
+ self._active_tile_source = None
|
|
273
|
+ self._max_age = 3600
|
|
274
|
+ self._append_tile_source(u'OSM-Mapnik', u'http://tile.openstreetmap.org', 0, 18)
|
|
275
|
+ self._append_tile_source(u'OSM-CycleMap', u'http://c.tile.opencyclemap.org/cycle', 0, 18)
|
|
276
|
+ try:
|
|
277
|
+ # install a callback if property 'Tilehandler' had been changed. it will be called with the new tilehandler information.
|
|
278
|
+ props.InstallPostSetCallback('Tilehandler', self._props_callback)
|
|
279
|
+ except:
|
|
280
|
+ #TODO: exception handling
|
|
281
|
+ pass
|
|
282
|
+
|
|
283
|
+ def _props_callback(self, tilehandler):
|
|
284
|
+ """
|
|
285
|
+ Routione which is called, if tilehandler information had been changed. It will set the new tilehandler information.
|
|
286
|
+ @param tilehandler: dictionary with tilehandler information
|
|
287
|
+ """
|
|
288
|
+ self.set_props(**tilehandler)
|
|
289
|
+
|
|
290
|
+ def set_props(self, source, max_age):
|
|
291
|
+ """
|
|
292
|
+ Routine to set the proxy information.
|
|
293
|
+ @param source: source for tiles.
|
|
294
|
+ @param max_age: maximum age for a tile till it will be refreshed.
|
|
295
|
+ """
|
|
296
|
+ self._set_default(source)
|
|
297
|
+ self._max_age = max_age
|
|
298
|
+
|
|
299
|
+ def _append_tile_source(self, name, url, min_zoom, max_zoom):
|
|
300
|
+ """
|
|
301
|
+ Routine to append a tilesource.
|
|
302
|
+ @param name: Name for this tilesource
|
|
303
|
+ @param url: URL for this tile source (without tile depending information e.g. zoom level, ...)
|
|
304
|
+ @param min_zoom: Minimum zoom level for this tilesource
|
|
305
|
+ @param max_zoom: Maximum zoom level for this tilesource
|
|
306
|
+ """
|
|
307
|
+ self[name] = self.tile_source(self.gfw, name, url, min_zoom, max_zoom)
|
|
308
|
+ if self._active_tile_source == None:
|
|
309
|
+ self._set_default(name)
|
|
310
|
+
|
|
311
|
+ def _set_default(self, name):
|
|
312
|
+ """
|
|
313
|
+ Routine to set the default tilesorce (by name).
|
|
314
|
+ @param name: Name for the default tilesource.
|
|
315
|
+ @return: True if name was available, False if not.
|
|
316
|
+ """
|
|
317
|
+ if name in self.keys():
|
|
318
|
+ self._active_tile_source = name
|
|
319
|
+ return True
|
|
320
|
+ else:
|
|
321
|
+ return False
|
|
322
|
+
|
|
323
|
+ def get_active_source(self):
|
|
324
|
+ """
|
|
325
|
+ Routine to get the Name of the active tile source.
|
|
326
|
+ @return: name of the active tile source
|
|
327
|
+ """
|
|
328
|
+ return self._active_tile_source
|
|
329
|
+
|
|
330
|
+ def get_max_age(self):
|
|
331
|
+ return self._max_age
|
|
332
|
+
|
|
333
|
+ def get_choices(self):
|
|
334
|
+ """
|
|
335
|
+ Routine to get the names of the possible tile sources.
|
|
336
|
+ @return: list of possible tile sources
|
|
337
|
+ """
|
|
338
|
+ return self.keys()
|
|
339
|
+
|
|
340
|
+ def get_zooms(self):
|
|
341
|
+ """
|
|
342
|
+ Routine to get a list of available zoom levels for this source.
|
|
343
|
+ @return: zoom levels as a list (e.g. [0,1,2,3]).
|
|
344
|
+ """
|
|
345
|
+ return self[self._active_tile_source].get_zooms()
|
|
346
|
+
|
|
347
|
+ def get_url(self, x, y, zoom_lvl):
|
|
348
|
+ """
|
|
349
|
+ Routine to get the url information for a specific tile.
|
|
350
|
+ @param x: horizontal tile number
|
|
351
|
+ @param y: vertical tile number
|
|
352
|
+ @param zoom_lvl: zoom level for the tile
|
|
353
|
+ @return: url to tile as string
|
|
354
|
+ """
|
|
355
|
+ return self[self._active_tile_source].get_url(x, y, zoom_lvl)
|
|
356
|
+
|
|
357
|
+ def get_tile(self, x, y, zoom_lvl, from_cache):
|
|
358
|
+ """
|
|
359
|
+ Routine to get a tile.
|
|
360
|
+ @param x: horizontal tile number
|
|
361
|
+ @param y: vertical tile number
|
|
362
|
+ @param zoom_lvl: zoom level for the tile
|
|
363
|
+ @param from_cache: if True the tile is from cache
|
|
364
|
+ @return: tile as Image
|
|
365
|
+ """
|
|
366
|
+ return self[self._active_tile_source].get_tile(x, y, zoom_lvl, self._max_age, from_cache)
|
|
367
|
+
|
|
368
|
+ def tile_num(self, coordinate, zoom):
|
|
369
|
+ """
|
|
370
|
+ Routine which calculates the needed tile for coordinates.
|
|
371
|
+ @param coordinate: geo.coordinate instance with geographic information.
|
|
372
|
+ @param zoom: zoom information for the needed tile
|
|
373
|
+ @return: return a tuple of two float values (x- and y-tile)
|
|
374
|
+ """
|
|
375
|
+ lat_rad = math.radians(coordinate[pylibs.geo.coordinate.LATITUDE])
|
|
376
|
+ n = 2.0 ** zoom
|
|
377
|
+ xtile = (coordinate[pylibs.geo.coordinate.LONGITUDE] + 180.0) / 360.0 * n
|
|
378
|
+ ytile = (1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n
|
|
379
|
+ return (xtile, ytile)
|
|
380
|
+
|
|
381
|
+ def coordinate(self, xtile, ytile, zoom):
|
|
382
|
+ """
|
|
383
|
+ Routine which calculates geographic information out of tile information.
|
|
384
|
+ @param xtile: number of the tile (x)
|
|
385
|
+ @param ytile: number of the tile (y)
|
|
386
|
+ @param zoom: zoom level
|
|
387
|
+ @return: geo.coordinate instance with the geographic information
|
|
388
|
+ """
|
|
389
|
+ n = 2.0 ** zoom
|
|
390
|
+ lon_deg = xtile / n * 360.0 - 180.0
|
|
391
|
+ lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
|
|
392
|
+ lat_deg = math.degrees(lat_rad)
|
|
393
|
+ return pylibs.geo.coordinate(lon=lon_deg, lat=lat_deg)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+class osm_map():
|
|
397
|
+ """
|
|
398
|
+ This is an Image including an osm map
|
|
399
|
+ """
|
|
400
|
+ def __init__(self, th, callback_refresh):
|
|
401
|
+ """
|
|
402
|
+ Init routine for osm_map
|
|
403
|
+ @param th: tile_handler class needed to get tiles
|
|
404
|
+ @param callback_refresh: function to call, if osm_map had been changed (for refresh view)
|
|
405
|
+ callback gets two arguments
|
|
406
|
+ * the image or None if no image is available yet
|
|
407
|
+ * a description text (of the finished issue)
|
|
408
|
+ @param border: additional border around specified area
|
|
409
|
+ @param vers: refresh behaviour as described in VER_*
|
|
410
|
+ """
|
|
411
|
+ self.th = th
|
|
412
|
+ self.callback_refresh = callback_refresh
|
|
413
|
+ self._del_map_values()
|
|
414
|
+
|
|
415
|
+ self.running = False
|
|
416
|
+ self.stoprequest = False
|
|
417
|
+
|
|
418
|
+ def _del_map_values(self):
|
|
419
|
+ """
|
|
420
|
+ routine to reset (to None) map definitions (e.g. map_resolution, zoom_lvl, view range
|
|
421
|
+ """
|
|
422
|
+ self._image = None
|
|
423
|
+ self._map_resolution = None
|
|
424
|
+ self._zoom_lvl = None
|
|
425
|
+ self._center_coordinate = None
|
|
426
|
+
|
|
427
|
+ def _set_map_values(self, map_resolution, zoom_lvl, center_coordinate):
|
|
428
|
+ def zoom_limitation(th, zoom):
|
|
429
|
+ if zoom < min(th.get_zooms()):
|
|
430
|
+ return min(th.get_zooms())
|
|
431
|
+ if zoom > max(th.get_zooms()):
|
|
432
|
+ return max(th.get_zooms())
|
|
433
|
+ return zoom
|
|
434
|
+ self._image = Image.new('RGB', map_resolution, 'white')
|
|
435
|
+ self._map_resolution = map_resolution
|
|
436
|
+ self._zoom_lvl = zoom_limitation(self.th, zoom_lvl)
|
|
437
|
+ self._center_coordinate = center_coordinate
|
|
438
|
+
|
|
439
|
+ def disable(self):
|
|
440
|
+ self.stop_now()
|
|
441
|
+ self.callback_refresh = None
|
|
442
|
+
|
|
443
|
+ def get_image(self):
|
|
444
|
+ return self._image
|
|
445
|
+
|
|
446
|
+ def get_map_resolution(self):
|
|
447
|
+ return self._map_resolution
|
|
448
|
+
|
|
449
|
+ def get_zoom_lvl(self):
|
|
450
|
+ return self._zoom_lvl
|
|
451
|
+
|
|
452
|
+ def get_center_coordinate(self):
|
|
453
|
+ return self._center_coordinate
|
|
454
|
+
|
|
455
|
+ def _paste_tile(self, tile, xy):
|
|
456
|
+ """
|
|
457
|
+ routine to paste a single tile at xy in the map image.
|
|
458
|
+ @param tile: tile to paste in the image
|
|
459
|
+ @param xy: position to paste the tile. also negative or too large values are
|
|
460
|
+ possible to paste just parts of a tile
|
|
461
|
+ """
|
|
462
|
+ try:
|
|
463
|
+ self._image.paste(tile, xy)
|
|
464
|
+ except:
|
|
465
|
+ print "exception in osm_map()._paste_tile"
|
|
466
|
+ #TODO: exception handling
|
|
467
|
+
|
|
468
|
+ def create_map_by_res_n_centercoord_n_zoomlvl(self, max_x_res, max_y_res, center_coordinate, zoom_lvl, cache_only=False):
|
|
469
|
+ """
|
|
470
|
+ routine to ...
|
|
471
|
+ @param max_x_res: maximum x resolution
|
|
472
|
+ @param max_y_res: maximum y resolution
|
|
473
|
+ @param center_coordinate: center coordinates (object of geo.coordinates)
|
|
474
|
+ @param zoom_lvl: zoom_level to use for d
|
|
475
|
+ """
|
|
476
|
+ if center_coordinate is not None and zoom_lvl is not None:
|
|
477
|
+ self._del_map_values()
|
|
478
|
+ #
|
|
479
|
+ # needed values for further calculations and map creation
|
|
480
|
+ #
|
|
481
|
+ self._set_map_values((max_x_res, max_y_res), zoom_lvl, center_coordinate)
|
|
482
|
+ self._create_map(cache_only)
|
|
483
|
+
|
|
484
|
+ def create_map_by_coord_n_zoomlvl(self, tl_coord, br_coord, zoom_lvl, cache_only=False):
|
|
485
|
+ """
|
|
486
|
+ @param tl_coord: top left coordinates (object of geo.coordinates)
|
|
487
|
+ @param br_coord: bottom right coordinates (object of geo.coordinates)
|
|
488
|
+ @param zoom_lvl: zoom_level to use for
|
|
489
|
+ """
|
|
490
|
+ center_coordinate = pylibs.geo.area(tl_coord, br_coord).center_pos()
|
|
491
|
+ tl_tile = self.th.tile_num(tl_coord, zoom_lvl)
|
|
492
|
+ br_tile = self.th.tile_num(br_coord, zoom_lvl)
|
|
493
|
+ max_x_res = int((br_tile[0] - tl_tile[0]) * self.th.TILE_SIZE)
|
|
494
|
+ max_y_res = int((br_tile[1] - tl_tile[1]) * self.th.TILE_SIZE)
|
|
495
|
+ self.create_map_by_res_n_centercoord_n_zoomlvl(max_x_res, max_y_res, center_coordinate, zoom_lvl, cache_only)
|
|
496
|
+
|
|
497
|
+ def create_map_by_res_n_coord(self, max_x_res, max_y_res, tl_coord, br_coord, cache_only=False):
|
|
498
|
+ """
|
|
499
|
+ @param max_x_res: maximum x resolution
|
|
500
|
+ @param max_y_res: maximum y resolution
|
|
501
|
+ @param tl_coord: top left coordinates (object of geo.coordinates)
|
|
502
|
+ @param br_coord: bottom right coordinates (object of geo.coordinates)
|
|
503
|
+
|
|
504
|
+ coord are not the used coordinated for the map corners, cause the zoom_lvl is quatisised
|
|
505
|
+ """
|
|
506
|
+ def coordinates_in_map(max_x, max_y, p1, p2, center, zoom_lvl):
|
|
507
|
+ tl = self.get_coord_by_xy(0, 0, (max_x, max_y), center, zoom_lvl)
|
|
508
|
+ br = self.get_coord_by_xy(max_x, max_y, (max_x, max_y), center, zoom_lvl)
|
|
509
|
+ area = pylibs.geo.area(tl, br)
|
|
510
|
+ return area.coordinate_in_area(p1) and area.coordinate_in_area(p2)
|
|
511
|
+ center_coordinate = pylibs.geo.area(tl_coord, br_coord).center_pos()
|
|
512
|
+ zoom_lvl = max(self.th.get_zooms())
|
|
513
|
+ while not coordinates_in_map(max_x_res, max_y_res, tl_coord, br_coord, center_coordinate, zoom_lvl):
|
|
514
|
+ zoom_lvl -= 1
|
|
515
|
+ self.create_map_by_res_n_centercoord_n_zoomlvl(max_x_res, max_y_res, center_coordinate, zoom_lvl, cache_only)
|
|
516
|
+
|
|
517
|
+ def stop_now(self):
|
|
518
|
+ self.stoprequest = True
|
|
519
|
+ while self.running:
|
|
520
|
+ pass
|
|
521
|
+ self.stoprequest = False
|
|
522
|
+
|
|
523
|
+ def get_coord_by_xy(self, x, y, map_resolution=None, center_coordinate=None, zoom_lvl=None):
|
|
524
|
+ zoom_lvl = zoom_lvl or self._zoom_lvl
|
|
525
|
+ tl_tile = self._get_tl_tile_num(map_resolution, center_coordinate, zoom_lvl)
|
|
526
|
+ xy_tile = (tl_tile[0] + float(x) / self.th.TILE_SIZE, tl_tile[1] + float(y) / self.th.TILE_SIZE)
|
|
527
|
+ return self.th.coordinate(xy_tile[0], xy_tile[1], zoom_lvl)
|
|
528
|
+
|
|
529
|
+ def get_xy_by_coord(self, coord, map_resolution=None, center_coordinate=None, zoom_lvl=None):
|
|
530
|
+ tl_tile = self._get_tl_tile_num(map_resolution, center_coordinate, zoom_lvl)
|
|
531
|
+ xy_tile = self.th.tile_num(coord, self._zoom_lvl)
|
|
532
|
+ x = int((xy_tile[0] - tl_tile[0]) * self.th.TILE_SIZE)
|
|
533
|
+ y = int((xy_tile[1] - tl_tile[1]) * self.th.TILE_SIZE)
|
|
534
|
+ return (x, y)
|
|
535
|
+
|
|
536
|
+ def _get_map_res_tiles(self, map_resolution=None):
|
|
537
|
+ """
|
|
538
|
+ returns the map resolution in number of tiles
|
|
539
|
+ """
|
|
540
|
+ map_resolution = map_resolution or self._map_resolution
|
|
541
|
+ if map_resolution:
|
|
542
|
+ return (map_resolution[0] / float(self.th.TILE_SIZE), map_resolution[1] / float(self.th.TILE_SIZE))
|
|
543
|
+ else:
|
|
544
|
+ return None
|
|
545
|
+
|
|
546
|
+ def _get_tl_tile_num(self, map_resolution=None, center_coordinate=None, zoom_lvl=None):
|
|
547
|
+ map_resolution = map_resolution or self._map_resolution
|
|
548
|
+ center_coordinate = center_coordinate or self._center_coordinate
|
|
549
|
+ zoom_lvl = zoom_lvl or self._zoom_lvl
|
|
550
|
+ #
|
|
551
|
+ if (map_resolution and center_coordinate and zoom_lvl):
|
|
552
|
+ center_tile_num = self.th.tile_num(center_coordinate, zoom_lvl)
|
|
553
|
+ map_resolution_tiles = self._get_map_res_tiles(map_resolution)
|
|
554
|
+ topleft_tile_num = (center_tile_num[0] - map_resolution_tiles[0] / 2, center_tile_num[1] - map_resolution_tiles[1] / 2)
|
|
555
|
+ return topleft_tile_num
|
|
556
|
+ else:
|
|
557
|
+ return None
|
|
558
|
+
|
|
559
|
+ def _get_br_tile_num(self, map_resolution=None, center_coordinate=None, zoom_lvl=None):
|
|
560
|
+ topleft_tile_num = self._get_tl_tile_num(map_resolution, center_coordinate, zoom_lvl)
|
|
561
|
+ map_resolution_tiles = self._get_map_res_tiles(map_resolution)
|
|
562
|
+ bottomright_tile_num = (topleft_tile_num[0] + map_resolution_tiles[0], topleft_tile_num[1] + map_resolution_tiles[1])
|
|
563
|
+ return bottomright_tile_num
|
|
564
|
+
|
|
565
|
+ def _get_xy_offset(self):
|
|
566
|
+ tl_tile = self._get_tl_tile_num()
|
|
567
|
+ x_offs = -int(tl_tile[0] % 1 * self.th.TILE_SIZE)
|
|
568
|
+ y_offs = -int(tl_tile[1] % 1 * self.th.TILE_SIZE)
|
|
569
|
+ return (x_offs, y_offs)
|
|
570
|
+
|
|
571
|
+ def _get_tile_list(self):
|
|
572
|
+ tl_tile = self._get_tl_tile_num()
|
|
573
|
+ br_tile = self._get_br_tile_num()
|
|
574
|
+ tile_list = []
|
|
575
|
+ for x in range(int(tl_tile[0]), int(br_tile[0]) + 1):
|
|
576
|
+ for y in range(int(tl_tile[1]), int(br_tile[1]) + 1):
|
|
577
|
+ tile_list.append((x, y, self._zoom_lvl))
|
|
578
|
+ return tile_list
|
|
579
|
+
|
|
580
|
+ def _create_map(self, cache_only):
|
|
581
|
+ """
|
|
582
|
+ @param resoultion: map target resolution
|
|
583
|
+ @param xy_offset: offset for top left tile (normally <= 0)
|
|
584
|
+ @param zoom_lvl: tile zoom_lvl
|
|
585
|
+ @param tile_list: list of tiles [[x1, x2, x3], [y1, y2]]
|
|
586
|
+ @param description: description text for callback function
|
|
587
|
+ """
|
|
588
|
+ def create_map_by_(xy_offset, tile_list, by_path):
|
|
589
|
+ #
|
|
590
|
+ # create map from already stored tiles (...by_path)
|
|
591
|
+ #
|
|
592
|
+ num_tiles = len(tile_list)
|
|
593
|
+ x0, y0 = tile_list[0][:2]
|
|
594
|
+ num = 0
|
|
595
|
+ for x, y, z in tile_list:
|
|
596
|
+ num += 1
|
|
597
|
+ if self.stoprequest:
|
|
598
|
+ break
|
|
599
|
+ tile = self.th.get_tile(x, y, z, by_path)
|
|
600
|
+ if tile != None:
|
|
601
|
+ # paste tile only if tile was available
|
|
602
|
+ pos = (xy_offset[0] + (x - x0) * self.th.TILE_SIZE, xy_offset[1] + (y - y0) * self.th.TILE_SIZE)
|
|
603
|
+ self._paste_tile(tile, pos)
|
|
604
|
+ if not by_path:
|
|
605
|
+ desc = "Tile " + self.th.get_url(x, y, z) + " added to map."
|
|
606
|
+ prog = float(num) / num_tiles
|
|
607
|
+ if self.callback_refresh:
|
|
608
|
+ self.callback_refresh(self._image, desc, prog)
|
|
609
|
+ self.running = True
|
|
610
|
+ #TODO: ggf. Klasse um Uebersetzungen zu ermoeglichen.
|
|
611
|
+
|
|
612
|
+ create_map_by_(self._get_xy_offset(), self._get_tile_list(), by_path=True)
|
|
613
|
+ desc = 'Map creation from cache completeled.'
|
|
614
|
+ if self.callback_refresh:
|
|
615
|
+ self.callback_refresh(self._image, desc, 1.0)
|
|
616
|
+ if not cache_only:
|
|
617
|
+ create_map_by_(self._get_xy_offset(), self._get_tile_list(), by_path=False)
|
|
618
|
+ desc = 'Map creation completeled.'
|
|
619
|
+ if self.callback_refresh:
|
|
620
|
+ self.callback_refresh(self._image, desc, 1.0)
|
|
621
|
+ self.running = False
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+def show_map(image, description, progress):
|
|
625
|
+ print description, "%5.1f%%" % (progress * 100.)
|
|
626
|
+ if image != None:
|
|
627
|
+ image.show()
|
|
628
|
+'''
|