Python Library GEO
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

osm.py 24KB

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