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

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

actually make that run in a thread... ;)

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