source: subversion/applications/routing/pyroute/tiles.py @ 5726

Last change on this file since 5726 was 5726, checked in by ojw, 12 years ago

minor cleanup

File size: 4.7 KB
Line 
1#!/usr/bin/python
2#-----------------------------------------------------------------------------
3# Tile image handler (download, cache, and display tiles)
4#
5# Usage:
6#   (library code for pyroute GUI, not for direct use)
7#-----------------------------------------------------------------------------
8# Copyright 2007, Oliver White
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program.  If not, see <http://www.gnu.org/licenses/>.
22#-----------------------------------------------------------------------------
23from base import pyrouteModule
24from tilenames import *
25import urllib
26import os
27import cairo
28from threading import Thread
29
30class tileLoader(Thread):
31  """Downloads images in a separate thread"""
32  def __init__(self, x,y,z,layer,filename):
33    """Download a tile image"""
34    self.url = tileURL(x,y,z)
35    self.finished = 0
36    self.filename = filename
37    Thread.__init__(self)
38   
39  def run(self):
40    # to emulate a normal web connection ;)
41    urllib.urlretrieve(self.url, self.filename)
42    self.finished = 1
43
44class tileHandler(pyrouteModule):
45  """Loads and displays map tiles"""
46  def __init__(self, modules):
47    pyrouteModule.__init__(self, modules)
48    self.images = {}
49    self.threads = {}
50   
51  def imageName(self,x,y,z,layer):
52    """Get a unique name for a tile image
53    (suitable for use as part of filenames, dictionary keys, etc)"""
54    return("%s_%d_%d_%d" % (layer,z,x,y))
55 
56  def loadImage(self,x,y,z, layer):
57    """Check that an image is loaded, and try to load it if not"""
58   
59    # First: is the image already in memory?
60    name = self.imageName(x,y,z,layer)
61    if name in self.images.keys():
62      return
63   
64    # Second, is it already in the process of being downloaded?
65    if name in self.threads.keys():
66      if(not self.threads[name].finished):
67        return
68   
69    # Third, is it in the disk cache?  (including ones recently-downloaded)
70    filename = "cache/%s.png" % name
71    if os.path.exists(filename):
72      self.images[name]  = cairo.ImageSurface.create_from_png(filename)
73      return
74   
75    # Image not found anywhere - resort to downloading it
76    print "Downloading %s" % (name)
77    self.threads[name] = tileLoader(x,y,z,layer,filename)
78    self.threads[name].start()
79   
80  def drawImage(self,cr, tile, bbox):
81    """Draw a tile image"""
82    name = self.imageName(tile[0],tile[1],tile[2], tile[3])
83    # If it's not in memory, then stop here
84    if not name in self.images.keys():
85      return
86    # Move the cairo projection onto the area where we want to draw the image
87    cr.save()
88    cr.translate(bbox[0],bbox[1])
89    cr.scale((bbox[2] - bbox[0]) / 256.0, (bbox[3] - bbox[1]) / 256.0)
90   
91    # Display the image
92    cr.set_source_surface(self.images[name],0,0)
93    cr.paint()
94   
95    # Return the cairo projection to what it was
96    cr.restore()
97   
98  def zoomFromScale(self,scale):
99    """Given a 'scale' (such as it is) from the projection module,
100    decide what zoom-level of map tiles to display"""
101    if(scale > 0.046):
102      return(10)
103    if(scale > 0.0085):
104      return(13)
105    if(scale > 0.0026):
106      return(15)
107    return(17)
108   
109  def tileZoom(self):
110    """Decide (based on global data) what zoom-level of map tiles to display"""
111    return(self.zoomFromScale(self.m['projection'].scale))
112 
113  def draw(self, cr):
114    """Draw all the map tiles that are in view"""
115    proj = self.m['projection']
116    layer = "tah"
117   
118    # Pick a zoom level
119    z = self.tileZoom()
120   
121    # Find out what tiles are in view at that zoom level
122    view_x1,view_y1 = latlon2xy(proj.N, proj.W, z)
123    view_x2,view_y2 = latlon2xy(proj.S, proj.E, z)
124   
125    # Loop through all tiles
126    for x in range(int(floor(view_x1)), int(ceil(view_x2))):
127      for y in range(int(floor(view_y1)), int(ceil(view_y2))):
128       
129        # Find the edges of the tile as lat/long
130        S,W,N,E = tileEdges(x,y,z) 
131       
132        # Convert those edges to screen coordinates
133        x1,y1 = proj.ll2xy(N,W)
134        x2,y2 = proj.ll2xy(S,E)
135       
136        # Check that the image is available in the memory cache
137        # and start downloading it if it's not
138        self.loadImage(x,y,z,layer)
139       
140        # Draw the image
141        self.drawImage(cr,(x,y,z,layer),(x1,y1,x2,y2))
Note: See TracBrowser for help on using the repository browser.