source: TI02-CSML/trunk/services/3rdParty/tilecache-1.4/TileCache/Layer.py @ 2202

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI02-CSML/trunk/services/3rdParty/tilecache-1.4/TileCache/Layer.py@2202
Revision 2202, 11.3 KB checked in by lawrence, 13 years ago (diff)

Adding tilecache to the "interesting" OGC services already implemented
in python.

Line 
1# BSD Licensed, Copyright (c) 2006 MetaCarta, Inc.
2import os, sys
3from warnings import warn
4from Client import WMS
5
6DEBUG = True
7
8class Tile (object):
9    __slots__ = ( "layer", "x", "y", "z", "data" )
10    def __init__ (self, layer, x, y, z):
11        self.layer = layer
12        self.x = x
13        self.y = y
14        self.z = z
15        self.data = None
16
17    def size (self):
18        return self.layer.size
19
20    def bounds (self):
21        res  = self.layer.resolutions[self.z]
22        minx = self.layer.bbox[0] + (res * self.x * self.layer.size[0])
23        miny = self.layer.bbox[1] + (res * self.y * self.layer.size[1])
24        maxx = self.layer.bbox[0] + (res * (self.x + 1) * self.layer.size[0])
25        maxy = self.layer.bbox[1] + (res * (self.y + 1) * self.layer.size[1])
26        return (minx, miny, maxx, maxy)
27
28    def bbox (self):
29        return ",".join(map(str, self.bounds()))
30
31class MetaTile (Tile):
32    def actualSize (self) :
33        metaCols, metaRows = self.layer.getMetaSize(self.z)
34        return ( self.layer.size[0] * metaCols,
35                 self.layer.size[1] * metaRows )
36
37    def size (self):
38        actual = self.actualSize()
39        return ( actual[0] + self.layer.metaBuffer * 2, 
40                 actual[1] + self.layer.metaBuffer * 2 )
41
42    def bounds (self):
43        tilesize   = self.actualSize()
44        res        = self.layer.resolutions[self.z]
45        buffer     = res * self.layer.metaBuffer
46        metaWidth  = res * tilesize[0]
47        metaHeight = res * tilesize[1]
48        minx = self.layer.bbox[0] + self.x * metaWidth  - buffer
49        miny = self.layer.bbox[1] + self.y * metaHeight - buffer
50        maxx = minx + metaWidth  + 2 * buffer
51        maxy = miny + metaHeight + 2 * buffer
52        return (minx, miny, maxx, maxy)
53
54class Layer (object):
55    __slots__ = ( "name", "layers", "bbox", 
56                  "size", "resolutions", "extension", "srs",
57                  "cache", "debug", "description" )
58    threshold = 0.001
59   
60    def __init__ (self, name, layers = None, bbox = (-180, -90, 180, 90),
61                        srs  = "EPSG:4326", description = "", maxresolution = None,
62                        size = (256, 256), levels = 20, resolutions = None,
63                        extension = "png", cache = None,  debug = True):
64        self.name   = name
65        self.description = description
66        self.layers = layers or name
67        if isinstance(bbox, str): bbox = map(float,bbox.split(","))
68        self.bbox = bbox
69        if isinstance(size, str): size = map(int,size.split(","))
70        self.size = size
71        self.srs  = srs
72        if extension.lower() == 'jpg': extension = 'jpeg' # MIME
73        self.extension = extension.lower()
74        if isinstance(debug, str):
75            debug = debug.lower() not in ("false", "off", "no", "0")
76        self.cache = cache
77        self.debug = debug
78        if resolutions:
79            if isinstance(resolutions, str):
80                resolutions = map(float,resolutions.split(","))
81            self.resolutions = resolutions
82        else:
83            maxRes = None
84            if not maxresolution:
85                width  = bbox[2] - bbox[0]
86                height = bbox[3] - bbox[1]
87                if width >= height:
88                    aspect = int( float(width) / height + .5 ) # round up
89                    maxRes = float(width) / (size[0] * aspect)
90                else:
91                    aspect = int( float(height) / width + .5 ) # round up
92                    maxRes = float(height) / (size[1] * aspect)
93            else:
94                maxRes = float(maxresolution)
95            self.resolutions = [maxRes / 2 ** i for i in range(levels)]
96
97    def getResolution (self, (minx, miny, maxx, maxy)):
98        return max( (maxx - minx) / self.size[0],
99                    (maxy - miny) / self.size[1] )
100
101    def getLevel (self, res):
102        z = None
103        for i in range(len(self.resolutions)):
104            if abs( self.resolutions[i] - res ) < 0.00000001:
105                res = self.resolutions[i]
106                z = i
107                break
108        if z is None and self.debug:
109            warn("can't find resolution index for %f" % res)
110        return z
111
112    def getClosestLevel (self, res):
113        for i in range(1, len(self.resolutions)):
114            if res > self.resolutions[i]:
115                return i - 1 
116        return len(self.resolutions) - 1
117
118    def getCell (self, (minx, miny, maxx, maxy), exact = True):
119        if exact and not self.contains((minx, miny)): 
120            if self.debug: warn(
121                "Lower left (%f, %f) is outside layer bounds" % (minx, miny))
122            return None
123
124        res = self.getResolution((minx, miny, maxx, maxy))
125        x = y = None
126
127        if exact:
128            z = self.getLevel(res)
129            if z is None: return None # oops
130        else:
131            z = self.getClosestLevel(res)
132
133        res = self.resolutions[z]
134        x0 = (minx - self.bbox[0]) / (res * self.size[0])
135        y0 = (miny - self.bbox[1]) / (res * self.size[1])
136       
137        x = int(x0 + self.threshold / 2)
138        y = int(y0 + self.threshold / 2)
139
140        if exact:
141            if abs(x - x0) > self.threshold:
142                if self.debug:
143                    warn("x (%f) - x0 (%f) = %f" % (x, x0, abs(x - x0)))
144                return None
145            if abs(y - y0) > self.threshold:
146                if self.debug:
147                    warn("y (%f) - y0 (%f) = %f" % (y, y0, abs(y - y0)))
148                return None
149
150        return (x, y, z)
151
152    def getClosestCell (self, z, (minx, miny)):
153        res = self.resolutions[z]
154        maxx = minx + self.size[0] * res
155        maxy = miny + self.size[1] * res
156        return self.getCell((minx, miny, maxx, maxy), False)
157
158    def getTile (self, bbox):
159        coord = self.getCell(bbox)
160        if not coord: return None
161        return Tile(self, *coord)
162
163    def contains (self, (x, y)):
164        return x >= self.bbox[0] and x <= self.bbox[2] \
165           and y >= self.bbox[1] and y <= self.bbox[3]
166
167    def grid (self, z):
168        width  = (self.bbox[2] - self.bbox[0]) / (self.resolutions[z] * self.size[0])
169        height = (self.bbox[3] - self.bbox[1]) / (self.resolutions[z] * self.size[1])
170        return (width, height)
171
172    def format (self):
173        return "image/" + self.extension
174   
175    def renderTile (self, tile):
176        # To be implemented by subclasses
177        pass 
178
179    def render (self, tile):
180        return self.renderTile(tile)
181
182class MetaLayer (Layer):
183    __slots__ = ('metaTile', 'metaSize', 'metaBuffer')
184    def __init__ (self, name, metatile = False, metasize = (5,5),
185                              metabuffer = 10, **kwargs):
186        Layer.__init__(self, name, **kwargs)
187        self.metaTile    = metatile
188        if isinstance(metasize, str):
189            metasize = map(int,metasize.split(","))
190        if isinstance(metabuffer, str):
191            metabuffer = int(metabuffer)
192        self.metaSize    = metasize
193        self.metaBuffer  = metabuffer
194
195    def getMetaSize (self, z):
196        maxcol, maxrow = self.grid(z)
197        return ( min(self.metaSize[0], int(maxcol)), 
198                 min(self.metaSize[1], int(maxrow)) )
199
200    def getMetaTile (self, tile):
201        x = int(tile.x / self.metaSize[0])
202        y = int(tile.y / self.metaSize[1])
203        return MetaTile(self, x, y, tile.z) 
204
205    def renderMetaTile (self, metatile, tile):
206        import StringIO, Image
207
208        data = self.renderTile(metatile)
209        image = Image.open( StringIO.StringIO(data) )
210
211        metaCols, metaRows = self.getMetaSize(metatile.z)
212        metaHeight = metaRows * self.size[1] + 2 * self.metaBuffer
213        for i in range(metaCols):
214            for j in range(metaRows):
215                minx = i * self.size[0] + self.metaBuffer
216                maxx = minx + self.size[0]
217                ### this next calculation is because image origin is (top,left)
218                maxy = metaHeight - (j * self.size[1] + self.metaBuffer)
219                miny = maxy - self.size[1]
220                subimage = image.crop((minx, miny, maxx, maxy))
221                buffer = StringIO.StringIO()
222                if image.info.has_key('transparency'): 
223                    subimage.save(buffer, self.extension, transparency=image.info['transparency'])
224                else:
225                    subimage.save(buffer, self.extension)
226                buffer.seek(0)
227                subdata = buffer.read()
228                x = metatile.x * self.metaSize[0] + i
229                y = metatile.y * self.metaSize[1] + j
230                subtile = Tile( self, x, y, metatile.z )
231                self.cache.set( subtile, subdata )
232                if x == tile.x and y == tile.y:
233                    tile.data = subdata
234
235        return tile.data
236
237    def render (self, tile):
238        if self.metaTile:
239            metatile = self.getMetaTile(tile)
240            try:
241                self.cache.lock(metatile)
242                image = self.cache.get(tile)
243                if not image:
244                    image = self.renderMetaTile(metatile, tile)
245            finally:
246                self.cache.unlock(metatile)
247            return image
248        else:
249            return self.renderTile(tile)
250
251class WMSLayer(MetaLayer):
252    def __init__ (self, name, url = None, **kwargs):
253        MetaLayer.__init__(self, name, **kwargs) 
254        self.url = url
255
256    def renderTile(self, tile):
257        wms = WMS( self.url, {
258          "bbox": tile.bbox(),
259          "width": tile.size()[0],
260          "height": tile.size()[1],
261          "srs": self.srs,
262          "format": self.format(),
263          "layers": self.layers,
264        } )
265        tile.data, response = wms.fetch()
266        return tile.data
267
268class MapnikLayer(MetaLayer):
269    def __init__ (self, name, mapfile = None, **kwargs):
270        MetaLayer.__init__(self, name, **kwargs) 
271        self.mapfile = mapfile
272        self.mapnik  = None
273
274    def renderTile(self, tile):
275        import mapnik, Image, StringIO
276       
277        if self.mapnik:
278            m = self.mapnik
279        else:
280            # Init it as 0,0
281            m = mapnik.Map( 0, 0 )
282            mapnik.load_map(m,self.mapfile)
283            # this will insure that it gets cached in mod_python
284            self.mapnik = m
285       
286        # Set the mapnik size to match the size of the current tile
287        m.width = tile.size()[0]
288        m.height = tile.size()[1]
289       
290        bbox = tile.bounds()
291        bbox = mapnik.Envelope(bbox[0], bbox[1], bbox[2], bbox[3])
292        m.zoom_to_box(bbox)
293                   
294        im = mapnik.Image( *tile.size() )
295        mapnik.render(m, im)
296        im = Image.fromstring('RGBA', tile.size(), mapnik.rawdata(im))
297        buffer = StringIO.StringIO()
298        im.save(buffer, self.extension)
299        buffer.seek(0)
300        tile.data = buffer.read()
301        return tile.data
302
303class MapServerLayer(MetaLayer):
304    def __init__ (self, name, mapfile = None, **kwargs):
305        MetaLayer.__init__(self, name, **kwargs) 
306        self.mapfile = mapfile
307
308    def renderTile(self, tile):
309        import mapscript
310        wms = mapscript.mapObj(self.mapfile) 
311        req = mapscript.OWSRequest()
312        req.setParameter("bbox", tile.bbox())
313        req.setParameter("width", str(tile.size()[0]))
314        req.setParameter("height", str(tile.size()[1]))
315        req.setParameter("srs", self.srs)
316        req.setParameter("format", self.format())
317        req.setParameter("layers", self.layers)
318        wms.loadOWSParameters(req)
319        mapImage = wms.draw()
320        tile.data = mapImage.getBytes()
321        return tile.data
Note: See TracBrowser for help on using the repository browser.