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

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

add layer names (not used in tileURL, but available in all the download/cache code)
+ remove temporary debugging text

File size: 4.6 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    self.run()
39   
40  def run(self):
41    sleep(5)
42    urllib.urlretrieve(self.url, self.filename)
43    self.finished = 1
44
45class tileHandler(pyrouteModule):
46  """Loads and displays map tiles"""
47  def __init__(self, modules):
48    pyrouteModule.__init__(self, modules)
49    self.images = {}
50    self.threads = {}
51   
52  def imageName(self,x,y,z,layer):
53    """Get a unique name for a tile image
54    (suitable for use as part of filenames, dictionary keys, etc)"""
55    return("%s_%d_%d_%d" % (layer,z,x,y))
56 
57  def loadImage(self,x,y,z, layer):
58    """Check that an image is loaded, and try to load it if not"""
59   
60    # First: is the image already in memory?
61    name = self.imageName(x,y,z,layer)
62    if name in self.images.keys():
63      return
64   
65    # Second, is it already in the process of being downloaded?
66    if name in self.threads.keys():
67      if(not self.threads[name].finished):
68        return
69   
70    # Third, is it in the disk cache?  (including ones recently-downloaded)
71    filename = "cache/%s.png" % name
72    if os.path.exists(filename):
73      self.images[name]  = cairo.ImageSurface.create_from_png(filename)
74      return
75   
76    # Image not found anywhere - resort to downloading it
77    print "Downloading %s" % name
78    self.threads[name] = tileLoader(x,y,z,layer,filename)
79   
80   
81  def drawImage(self,cr, tile, bbox):
82    """Draw a tile image"""
83    name = self.imageName(tile[0],tile[1],tile[2], tile[3])
84    # If it's not in memory, then stop here
85    if not name in self.images.keys():
86      return
87    # Move the cairo projection onto the area where we want to draw the image
88    cr.save()
89    cr.translate(bbox[0],bbox[1])
90    cr.scale((bbox[2] - bbox[0]) / 256.0, (bbox[3] - bbox[1]) / 256.0)
91   
92    # Display the image
93    cr.set_source_surface(self.images[name],0,0)
94    cr.paint()
95   
96    # Return the cairo projection to what it was
97    cr.restore()
98   
99  def zoomFromScale(self,scale):
100    """Given a 'scale' (such as it is) from the projection module,
101    decide what zoom-level of map tiles to display"""
102    if(scale > 0.046):
103      return(10)
104    if(scale > 0.0085):
105      return(13)
106    if(scale > 0.0026):
107      return(15)
108    return(17)
109   
110  def tileZoom(self):
111    """Decide (based on global data) what zoom-level of map tiles to display"""
112    return(self.zoomFromScale(self.m['projection'].scale))
113 
114  def draw(self, cr):
115    """Draw all the map tiles that are in view"""
116    proj = self.m['projection']
117    layer = "tah"
118   
119    # Pick a zoom level
120    z = self.tileZoom()
121   
122    # Find out what tiles are in view at that zoom level
123    view_x1,view_y1 = latlon2xy(proj.N, proj.W, z)
124    view_x2,view_y2 = latlon2xy(proj.S, proj.E, z)
125   
126    # Loop through all tiles
127    for x in range(int(floor(view_x1)), int(ceil(view_x2))):
128      for y in range(int(floor(view_y1)), int(ceil(view_y2))):
129       
130        # Find the edges of the tile as lat/long
131        S,W,N,E = tileEdges(x,y,z) 
132       
133        # Convert those edges to screen coordinates
134        x1,y1 = proj.ll2xy(N,W)
135        x2,y2 = proj.ll2xy(S,E)
136       
137        # Check that the image is available in the memory cache
138        # and start downloading it if it's not
139        self.loadImage(x,y,z,layer)
140       
141        # Draw the image
142        self.drawImage(cr,(x,y,z,layer),(x1,y1,x2,y2))
Note: See TracBrowser for help on using the repository browser.