[Tilecache] Reduce the size of the PNGs (2/2)

Guillaume Lathoud glathoud at yahoo.fr
Tue Jul 15 09:23:24 EDT 2008


##################################################
# Modified TileCache/Layer.py
##################################################

# BSD Licensed, Copyright (c) 2006-2008 MetaCarta, Inc.

import os, sys
from warnings import warn
from Client import WMS
from Service import TileCacheException

DEBUG = True

class Tile (object):
    """
    >>> l = Layer("name", maxresolution=0.019914, size="256,256")
    >>> t = Tile(l, 18, 20, 0)
    """
    __slots__ = ( "layer", "x", "y", "z", "data" )
    def __init__ (self, layer, x, y, z):
        """
        >>> l = Layer("name", maxresolution=0.019914, size="256,256")
        >>> t = Tile(l, 18, 20, 0)
        >>> t.x 
        18
        >>> t.y
        20
        >>> t.z
        0
        >>> print t.data
        None
        """
        self.layer = layer
        self.x = x
        self.y = y
        self.z = z
        self.data = None

    def size (self):
        """
        >>> l = Layer("name", maxresolution=0.019914, size="256,256")
        >>> t = Tile(l, 18, 20, 0)
        >>> t.size()
        [256, 256]
        """
        return self.layer.size

    def bounds (self):
        """
        >>> l = Layer("name", maxresolution=0.019914)
        >>> t = Tile(l, 18, 20, 0)
        >>> t.bounds()
        (-88.236288000000002, 11.959680000000006, -83.138303999999991, 17.057664000000003)
        """
        res  = self.layer.resolutions[self.z]
        minx = self.layer.bbox[0] + (res * self.x * self.layer.size[0])
        miny = self.layer.bbox[1] + (res * self.y * self.layer.size[1])
        maxx = self.layer.bbox[0] + (res * (self.x + 1) * self.layer.size[0])
        maxy = self.layer.bbox[1] + (res * (self.y + 1) * self.layer.size[1])
        return (minx, miny, maxx, maxy)

    def bbox (self):
        """
        >>> l = Layer("name", maxresolution=0.019914)
        >>> t = Tile(l, 18, 20, 0)
        >>> t.bbox()
        '-88.236288,11.95968,-83.138304,17.057664'
        """
        return ",".join(map(str, self.bounds()))

class MetaTile (Tile):
    def actualSize (self):
        """
        >>> l = MetaLayer("name")
        >>> t = MetaTile(l, 0,0,0)
        >>> t.actualSize()
        (256, 256)
        """
        metaCols, metaRows = self.layer.getMetaSize(self.z)
        return ( self.layer.size[0] * metaCols,
                 self.layer.size[1] * metaRows )

    def size (self):
        actual = self.actualSize()
        return ( actual[0] + self.layer.metaBuffer[0] * 2, 
                 actual[1] + self.layer.metaBuffer[1] * 2 )

    def bounds (self):
        tilesize   = self.actualSize()
        res        = self.layer.resolutions[self.z]
        buffer     = (res * self.layer.metaBuffer[0], res * self.layer.metaBuffer[1])
        metaWidth  = res * tilesize[0]
        metaHeight = res * tilesize[1]
        minx = self.layer.bbox[0] + self.x * metaWidth  - buffer[0]
        miny = self.layer.bbox[1] + self.y * metaHeight - buffer[1]
        maxx = minx + metaWidth  + 2 * buffer[0]
        maxy = miny + metaHeight + 2 * buffer[1]
        return (minx, miny, maxx, maxy)

class Layer (object):
    __slots__ = ( "name", "layers", "bbox", "data_extent", 
                  "size", "resolutions", "extension", "srs",
                  "cache", "debug", "description", 
                  "watermarkimage", "watermarkopacity",
                  "extent_type", "tms_type", "units", "mime_type",
                  "spherical_mercator", "metadata", # modified_antialias
                  "antialias",    # modified_antialias
                  "palette",   # modified_palette
                  "pngcrush",  # modified_pngcrush
                  "jpegquality", # modified_jpegquality
                  )
    
    config_properties = [
      {'name':'spherical_mercator', 'description':'Layer is in spherical mercator. (Overrides bbox, maxresolution, SRS, Units)', 'type': 'boolean'},
      {'name':'layers', 'description': 'Comma seperated list of layers associated with this layer.'},
      {'name':'extension', 'description':'File type extension', 'default':'png'},
      {'name':'bbox', 'description':'Bounding box of the layer grid', 'default':'-180,-90,180,90'},
      {'name':'srs', 'description':'Spatial Reference System for the layer', 'default':'EPSG:4326'},
      {'name':'data_extent', 'description':'Bounding box of the layer data. (Same SRS as the layer grid.)', 'default':"", 'type': 'map'},
    ]  
    
    def __init__ (self, name, layers = None, bbox = (-180, -90, 180, 90),
                        data_extent = None,
                        srs  = "EPSG:4326", description = "", maxresolution = None,
                        size = (256, 256), levels = 20, resolutions = None,
                        extension = "png", mime_type = None, cache = None,  debug = True, 
                        watermarkimage = None, watermarkopacity = 0.2,
                        spherical_mercator = False,
                        extent_type = "strict", units = "degrees", tms_type = "", # modified_antialias
                  antialias = False,  # modified_antialias
                  palette = False,   # modified_palette
                  pngcrush = False,  # modified_pngcrush
                  jpegquality = False, # modified_jpegquality
                  **kwargs ):
        """Take in parameters, usually from a config file, and create a Layer.
        
        >>> l = Layer("Name", bbox="-12,17,22,36", debug="no")
        >>> l.bbox
        [-12.0, 17.0, 22.0, 36.0]
        >>> l.debug
        False
        
        >>> l = Layer("name", spherical_mercator="yes")
        >>> round(l.resolutions[0])
        156543.0
        """
        
        self.name   = name
        self.description = description
        self.layers = layers or name
        
        self.spherical_mercator = spherical_mercator and spherical_mercator.lower() in ["yes", "y", "t", "true"]
        if self.spherical_mercator:
            bbox = "-20037508.34,-20037508.34,20037508.34,20037508.34"
            maxresolution = "156543.0339"
            if srs == "EPSG:4326":
                srs = "EPSG:900913"
            units = "meters"

        if isinstance(bbox, str): 
            bbox = map(float, bbox.split(","))
        self.bbox = bbox
        
        if isinstance(data_extent, str): 
            data_extent = map(float, data_extent.split(","))
        self.data_extent = data_extent or bbox
        
        if isinstance(size, str): 
            size = map(int, size.split(","))
        self.size = size
        
        self.units = units
        
        self.srs  = srs
        
        if extension.lower() == 'jpg': 
            extension = 'jpeg' # MIME
        self.extension = extension.lower()
        self.mime_type = mime_type or self.format() 
        
        if isinstance(debug, str):
            debug = debug.lower() not in ("false", "off", "no", "0")
        self.debug = debug
        
        self.cache = cache
        self.extent_type = extent_type
        self.tms_type = tms_type
        
        if resolutions:
            if isinstance(resolutions, str):
                resolutions = map(float,resolutions.split(","))
            self.resolutions = resolutions
        else:
            maxRes = None
            if not maxresolution:
                width  = bbox[2] - bbox[0]
                height = bbox[3] - bbox[1]
                if width >= height:
                    aspect = int( float(width) / height + .5 ) # round up
                    maxRes = float(width) / (size[0] * aspect)
                else:
                    aspect = int( float(height) / width + .5 ) # round up
                    maxRes = float(height) / (size[1] * aspect)
            else:
                maxRes = float(maxresolution)
            self.resolutions = [maxRes / 2 ** i for i in range(int(levels))]
        
        self.watermarkimage = watermarkimage
        
        self.watermarkopacity = float(watermarkopacity)

        # modified_antialias begin
        if isinstance( antialias, str ):    
            antialias = antialias.lower() in ("true","yes","on","1")
        self.antialias = antialias
        # modified_antialias end

        # modified_palette begin
        self.palette = False
        if isinstance( palette, str ):
            if len( palette ) > 1:
                if ( palette[ 0 ] == palette[ -1 ] ) and ( palette[ 0 ] in ( '"', "'", ) ):
                    palette = palette[ 1:-1 ]
                self.palette = palette
        # modified_palette end
        
        # modified_pngcrush begin
        self.pngcrush = False
        if isinstance( pngcrush, str ):
            if len( pngcrush ) > 1:
                if ( pngcrush[ 0 ] == pngcrush[ -1 ] ) and ( pngcrush[ 0 ] in ( '"', "'", ) ):
                    pngcrush = pngcrush[ 1:-1 ]
                self.pngcrush = pngcrush
        # modified_pngcrush end

        # modified_jpegquality begin
        self.jpegquality = False
        if isinstance( jpegquality, str ):
            jpegquality = int( jpegquality )
        if isinstance( jpegquality, int ):
            self.jpegquality = jpegquality
        # modified_jpegquality end
        
        self.metadata = {}

        prefix_len = len("metadata_")
        for key in kwargs:
            if key.startswith("metadata_"):
                self.metadata[key[prefix_len:]] = kwargs[key]
                
                

    def getResolution (self, (minx, miny, maxx, maxy)):
        """
        >>> l = Layer("name")
        >>> l.getResolution((-180,-90,0,90))
        0.703125
        """
        return max( float(maxx - minx) / self.size[0],
                    float(maxy - miny) / self.size[1] )

    def getClosestLevel (self, res, size = [256, 256]):
        diff = sys.maxint
        z = None
        for i in range(len(self.resolutions)):
            if diff > abs( self.resolutions[i] - res ):
                diff = abs( self.resolutions[i] - res ) 
                z = i
        return z

    def getLevel (self, res, size = [256, 256]):
        """
        >>> l = Layer("name")
        >>> l.getLevel(.703125)
        0
        """

        max_diff = res / max(size[0], size[1])
        z = None
        for i in range(len(self.resolutions)):
            if abs( self.resolutions[i] - res ) < max_diff:
                res = self.resolutions[i]
                z = i
                break
        if z is None:
            raise TileCacheException("can't find resolution index for %f. Available resolutions are: \n%s" % (res, self.resolutions))
        return z

    def getCell (self, (minx, miny, maxx, maxy), exact = True):
        """
        Returns x, y, z

        >>> l = Layer("name")
        >>> l.bbox
        (-180, -90, 180, 90)
        >>> l.resolutions[0]
        0.703125
        >>> l.getCell((-180.,-90.,0.,90.))
        (0, 0, 0)
        >>> l.getCell((-45.,-45.,0.,0.))
        (3, 1, 2)
        """
        res = self.getResolution((minx, miny, maxx, maxy))
        x = y = None

        if exact:
            z = self.getLevel(res, self.size)
        else:
            z = self.getClosestLevel(res, self.size)

        res = self.resolutions[z]
        
        if exact and self.extent_type == "strict" and not self.contains((minx, miny), res): 
            raise TileCacheException("Lower left corner (%f, %f) is outside layer bounds %s. \nTo remove this condition, set extent_type=loose in your configuration." 
                     % (minx, miny, self.bbox))
            return None

        x0 = (minx - self.bbox[0]) / (res * self.size[0])
        y0 = (miny - self.bbox[1]) / (res * self.size[1])
        
        x = int(round(x0))
        y = int(round(y0))
        
        tilex = ((x * res * self.size[0]) + self.bbox[0])
        tiley = ((y * res * self.size[1]) + self.bbox[1])
        if exact:
            if (abs(minx - tilex)  / res > 1):
                raise TileCacheException("Current x value %f is too far from tile corner x %f" % (minx, tilex))  
            
            if (abs(miny - tiley)  / res > 1):
                raise TileCacheException("Current y value %f is too far from tile corner y %f" % (miny, tiley))  
        
        return (x, y, z)

    def getClosestCell (self, z, (minx, miny)):
        """
        >>> l = Layer("name")
        >>> l.getClosestCell(2, (84, 17))
        (6, 2, 2)
        """
        res = self.resolutions[z]
        maxx = minx + self.size[0] * res
        maxy = miny + self.size[1] * res
        return self.getCell((minx, miny, maxx, maxy), False)

    def getTile (self, bbox):
        """
        >>> l = Layer("name")
        >>> l.getTile((-180,-90,0,90)).bbox()
        '-180.0,-90.0,0.0,90.0'
        """
        
        coord = self.getCell(bbox)
        if not coord: return None
        return Tile(self, *coord)

    def contains (self, (x, y), res = 0):
        """
        >>> l = Layer("name")
        >>> l.contains((0,0))
        True
        >>> l.contains((185, 94))
        False
        """
        diff_x1 = abs(x - self.bbox[0])
        diff_x2 = abs(x - self.bbox[2])
        diff_y1 = abs(y - self.bbox[1]) 
        diff_y2 = abs(y - self.bbox[3]) 
        return (x >= self.bbox[0] or diff_x1 < res) and (x <= self.bbox[2] or diff_x2 < res) \
           and (y >= self.bbox[1] or diff_y1 < res) and (y <= self.bbox[3] or diff_y2 < res)

    def grid (self, z):
        """
        Returns size of grid at a particular zoom level

        >>> l = Layer("name")
        >>> l.grid(3)
        (16.0, 8.0)
        """
        width  = (self.bbox[2] - self.bbox[0]) / (self.resolutions[z] * self.size[0])
        height = (self.bbox[3] - self.bbox[1]) / (self.resolutions[z] * self.size[1])
        return (width, height)

    def format (self):
        """
        >>> l = Layer("name")
        >>> l.format()
        'image/png'
        """
        return "image/" + self.extension
    
    def renderTile (self, tile):
        # To be implemented by subclasses
        pass 

    def render (self, tile):

        return self.renderTile(tile)
   
class MetaLayer (Layer):
    __slots__ = ('metaTile', 'metaSize', 'metaBuffer')
    
    config_properties = Layer.config_properties + [
      {'name':'name', 'description': 'Name of Layer'}, 
      {'name':'metaTile', 'description': 'Should metatiling be used on this layer?', 'default': 'false', 'type':'boolean'},
      {'name': 'metaSize', 'description': 'Comma seperated-pair of numbers, defininig the tiles included in a single size', 'default': "5,5"},
      {'name': 'metaBuffer', 'description': 'Number of pixels outside the metatile to include in the render request.'}
    ]  


    def __init__ (self, name, metatile = "", metasize = (5,5),
                              metabuffer = (10,10), **kwargs):
        Layer.__init__(self, name, **kwargs)
        self.metaTile    = metatile.lower() in ("true", "yes", "1")
        if isinstance(metasize, str):
            metasize = map(int,metasize.split(","))
        if isinstance(metabuffer, str):
            metabuffer = map(int, metabuffer.split(","))
            if len(metabuffer) == 1:
                metabuffer = (metabuffer[0], metabuffer[0])
        self.metaSize    = metasize
        self.metaBuffer  = metabuffer

    def getMetaSize (self, z):
        if not self.metaTile: return (1,1)
        maxcol, maxrow = self.grid(z)
        return ( min(self.metaSize[0], int(maxcol + 1)), 
                 min(self.metaSize[1], int(maxrow + 1)) )

    def getMetaTile (self, tile):
        x = int(tile.x / self.metaSize[0])
        y = int(tile.y / self.metaSize[1])
        return MetaTile(self, x, y, tile.z) 

    def renderMetaTile (self, metatile, tile):
        import StringIO, Image

        data = self.renderTile(metatile)
        image = Image.open( StringIO.StringIO(data) )

        metaCols, metaRows = self.getMetaSize(metatile.z)
        metaHeight = metaRows * self.size[1] + 2 * self.metaBuffer[1]
        for i in range(metaCols):
            for j in range(metaRows):
                minx = i * self.size[0] + self.metaBuffer[0]
                maxx = minx + self.size[0]
                ### this next calculation is because image origin is (top,left)
                maxy = metaHeight - (j * self.size[1] + self.metaBuffer[1])
                miny = maxy - self.size[1]
                subimage = image.crop((minx, miny, maxx, maxy))
                buffer = StringIO.StringIO()
                if image.info.has_key('transparency'): 
                    subimage.save(buffer, self.extension, transparency=image.info['transparency'])
                else:
                    subimage.save(buffer, self.extension)
                buffer.seek(0)
                subdata = buffer.read()
                x = metatile.x * self.metaSize[0] + i
                y = metatile.y * self.metaSize[1] + j
                subtile = Tile( self, x, y, metatile.z )
                if self.watermarkimage:
                    subdata = self.watermark(subdata)
                self.cache.set( subtile, subdata )
                if x == tile.x and y == tile.y:
                    tile.data = subdata

        return tile.data

    def render (self, tile):
        if self.metaTile:
            metatile = self.getMetaTile(tile)
            try:
                self.cache.lock(metatile)
                image = self.cache.get(tile)
                if not image:
                    image = self.renderMetaTile(metatile, tile)
            finally:
                self.cache.unlock(metatile)
            return image
        else:
            if self.watermarkimage:
                return self.watermark(self.renderTile(tile))
            else:
                return self.renderTile(tile)

    def watermark (self, img):
        import StringIO, Image, ImageEnhance
        tileImage = Image.open( StringIO.StringIO(img) )
        wmark = Image.open(self.watermarkimage)
        assert self.watermarkopacity >= 0 and self.watermarkopacity <= 1
        if wmark.mode != 'RGBA':
            wmark = wmark.convert('RGBA')
        else:
            wmark = wmark.copy()
        alpha = wmark.split()[3]
        alpha = ImageEnhance.Brightness(alpha).enhance(self.watermarkopacity)
        wmark.putalpha(alpha)
        if tileImage.mode != 'RGBA':
            tileImage = tileImage.convert('RGBA')
        watermarkedImage = Image.new('RGBA', tileImage.size, (0,0,0,0))
        watermarkedImage.paste(wmark, (0,0))
        watermarkedImage = Image.composite(watermarkedImage, tileImage, watermarkedImage)
        buffer = StringIO.StringIO()
        if watermarkedImage.info.has_key('transparency'):
            watermarkedImage.save(buffer, self.extension, transparency=compositeImage.info['transparency'])
        else:
            watermarkedImage.save(buffer, self.extension)
        buffer.seek(0)
        return buffer.read()

if __name__ == "__main__":
    import doctest
    doctest.testmod()


##################################################
# Modified TileCache/Client.py
##################################################

#!/usr/bin/env python

# BSD Licensed, Copyright (c) 2006-2008 MetaCarta, Inc.

import sys, urllib, urllib2, time, os, math
import httplib
import copy # modified_antialias

# setting this to True will exchange more useful error messages
# for privacy, hiding URLs and error messages.
HIDE_ALL = False 

class WMS (object):
    fields = ("bbox", "srs", "width", "height", "format", "layers", "styles",    # modified_antialias
              'antialias', 'extension',      # modified_antialias
              )
    defaultParams = {'version': '1.1.1', 'request': 'GetMap', 'service': 'WMS',  # modified_antialias
                     'antialias': False, 'extension':'png',   # modified_antialias
                     }
    __slots__ = ("base", "params", "client", "data", "response")

    def __init__ (self, base, params, user=None, password=None):
        self.base    = base
        if self.base[-1] not in "?&":
            if "?" in self.base:
                self.base += "&"
            else:
                self.base += "?"

        self.params  = {}
        if user is not None and password is not None:
           x = urllib2.HTTPPasswordMgrWithDefaultRealm()
           x.add_password(None, base, user, password)
           self.client  = urllib2.build_opener()
           auth = urllib2.HTTPBasicAuthHandler(x)
           self.client  = urllib2.build_opener(auth)
        else:
           self.client  = urllib2.build_opener()

        for key, val in self.defaultParams.items():
            if self.base.lower().rfind("%s=" % key.lower()) == -1:
                self.params[key] = val
        for key in self.fields:
            if params.has_key(key):
                self.params[key] = params[key]
            elif self.base.lower().rfind("%s=" % key.lower()) == -1:
                self.params[key] = ""

    def url (self):

        # modified_antialias begin

        params_copy = copy.deepcopy( self.params )

        if self.params[ 'antialias' ]:
            params_copy[ 'width' ]  *= 4
            params_copy[ 'height' ] *= 4

        for a_str in ( 'antialias',
                       'palette',    # modified_palette
                       'pngcrush',   # modified_pngcrush
                       'jpegquality', # modified_jpegquality
                       ):
            while a_str in params_copy:
                del params_copy[ a_str ]

        return self.base + urllib.urlencode( params_copy )

        # modified_antialias end
    
    def fetch (self):

        urlrequest = urllib2.Request(self.url())
        # urlrequest.add_header("User-Agent",
        #    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" )
        response = None
        while response is None:
            try:
                response = self.client.open(urlrequest)
                data = response.read()
                # check to make sure that we have an image...
                msg = response.info()
                if msg.has_key("Content-Type"):
                    ctype = msg['Content-Type']
                    if ctype[:5].lower() != 'image':
                        if HIDE_ALL:
                            raise Exception("Did not get image data back. (Adjust HIDE_ALL for more detail.)")
                        else:
                            raise Exception("Did not get image data back. \nURL: %s\nContent-Type Header: %s\nResponse: \n%s" % (self.url(), ctype, data))
            except httplib.BadStatusLine:
                response = None # try again

        # modified_antialias begin

        if self.params[ 'antialias' ]:     

            import StringIO, Image
        
            try:
                image = Image.open( StringIO.StringIO( data ) )    
                newimage = image.resize( ( self.params[ 'width' ], self.params[ 'height' ] ), Image.ANTIALIAS )    
                antialias_out = StringIO.StringIO()    

                if newimage.info.has_key('transparency'):    
                    newimage.save(antialias_out, self.params[ 'extension' ], 
                                  transparency=image.info['transparency'])   
                else:    
                    newimage.save(antialias_out, self.params[ 'extension' ])     

                # Replace the (e.g. 1024x1024) image data with the (e.g. 256x256) image data    
                data = antialias_out.getvalue()   

            except StandardError, e:
                import tempfile, os
                open( os.path.join( 'c:/tmp', 'antialias_error.txt' ), 'wt' ).write( str( e ) )
                raise

        # modified_antialias end
        
        return data, response

    def setBBox (self, box):
        self.params["bbox"] = ",".join(map(str, box))

def seed (svc, base, layer, levels = (0, 5), bbox = None):
    from Layer import Tile

    if not bbox: bbox = layer.bbox

    start = time.time()
    total = 0
    
    for z in range(*levels):
        bottomleft = layer.getClosestCell(z, bbox[0:2])
        topright   = layer.getClosestCell(z, bbox[2:4])
        print >>sys.stderr, "###### %s, %s" % (bottomleft, topright)
        zcount = 0 
        metaSize = layer.getMetaSize(z)
        ztiles = int(math.ceil(float(topright[1] - bottomleft[1]) / metaSize[0]) * math.ceil(float(topright[0] - bottomleft[0]) / metaSize[1])) 
        for y in range(bottomleft[1], topright[1], metaSize[1]):
            for x in range(bottomleft[0], topright[0], metaSize[0]):
                tileStart = time.time()
                tile = Tile(layer,x,y,z)
                bounds = tile.bounds()
                svc.renderTile(tile)
                total += 1
                zcount += 1
                box = "(%.4f %.4f %.4f %.4f)" % bounds
                print "%02d (%06d, %06d) = %s [%.4fs : %.3f/s] %s/%s" \
                     % (z,x,y, box, time.time() - tileStart, total / (time.time() - start + .0001), zcount, ztiles)

def main ():
    from Service import Service, cfgfiles
    from Layer import Layer
    base  = sys.argv[1]
    svc = Service.load(*cfgfiles)
    layer = svc.layers[sys.argv[2]]
    if len(sys.argv) == 5:
        seed(svc, base, layer, map(int, sys.argv[3:]))
    elif len(sys.argv) == 6:
        seed(svc, base, layer, map(int, sys.argv[3:5]), map(float, sys.argv[5].split(",")))
    else:
        for line in sys.stdin.readlines():
            lat, lon, delta = map(float, line.split(","))
            bbox = (lon - delta, lat - delta, lon + delta, lat + delta)
            print "===> %s <===" % (bbox,)
            seed(svc, base, layer, (5, 17), bbox)

if __name__ == '__main__':
    main()


##################################################
# Modified TileCache/Layers/WMS.py
##################################################

# BSD Licensed, Copyright (c) 2006-2008 MetaCarta, Inc.

from TileCache.Layer import MetaLayer
import TileCache.Client as WMSClient

class WMS(MetaLayer):
    config_properties = [
      {'name':'name', 'description': 'Name of Layer'}, 
      {'name':'url', 'description': 'URL of Remote Layer'},
      {'name':'user', 'description': 'Username of remote server: used for basic-auth protected backend WMS layers.'},
      {'name':'password', 'description': 'Password of remote server: Use for basic-auth protected backend WMS layers.'},
    ] + MetaLayer.config_properties  
     
    def __init__ (self, name, url = None, user = None, password = None, **kwargs):
        MetaLayer.__init__(self, name, **kwargs) 
        self.url = url
        self.user = user
        self.password = password

    def renderTile(self, tile):
        wms = WMSClient.WMS( self.url, {
          "bbox": tile.bbox(),
          "width": tile.size()[0],
          "height": tile.size()[1],
          "srs": self.srs,
          "format": self.format(),
          "layers": self.layers,
          'antialias': self.antialias, # modified_antialias
          'extension': self.extension, # modified_antialias
        }, self.user, self.password)
        tile.data, response = wms.fetch()
        return tile.data 


##################################################
# Modified TileCache/Layers/Image.py
##################################################

# BSD Licensed, Copyright (c) 2006-2008 MetaCarta, Inc.

from TileCache.Layer import MetaLayer

class Image(MetaLayer):
    """The ImageLayer allows you to set up any image file in TileCache.
       All you need is an image, and a geographic bounds (filebounds),
       Which is passed in as a single, comma seperated string in the form 
       minx,miny,maxx,maxy."""
    
    config_properties = [
      {'name':'name', 'description': 'Name of Layer'}, 
      {'name':'file', 'description': 'Location of PIL-readable file.'},
    ] + MetaLayer.config_properties 
    
    def __init__ (self, name, file = None, filebounds = "-180,-90,180,90",
                              transparency = False, scaling = "nearest",
                  fillcolor=None, # modified_imagelayerfillcolor
                  **kwargs):
        import PIL.Image as PILImage
        
        MetaLayer.__init__(self, name, **kwargs) 
        
        self.file = file
        self.filebounds  = map(float,filebounds.split(","))
        self.image = PILImage.open(self.file)
        self.image_size = self.image.size
        self.image_res = [(self.filebounds[2] - self.filebounds[0]) / self.image_size[0], 
                    (self.filebounds[3] - self.filebounds[1]) / self.image_size[1]
                   ]
        self.scaling = scaling.lower()
        if isinstance(transparency, str):
            transparency = transparency.lower() in ("true", "yes", "1")
        self.transparency = transparency
        self.fillcolor = fillcolor  # modified_imagelayerfillcolor

    def renderTile(self, tile):
        import PIL.Image as PILImage 
        import StringIO
        bounds = tile.bounds()
        size = tile.size()
        min_x = (bounds[0] - self.filebounds[0]) / self.image_res[0]   
        min_y = (self.filebounds[3] - bounds[3]) / self.image_res[1]
        max_x = (bounds[2] - self.filebounds[0]) / self.image_res[0]   
        max_y = (self.filebounds[3] - bounds[1]) / self.image_res[1]
        if self.scaling == "bilinear":
            scaling = PILImage.BILINEAR
        elif self.scaling == "bicubic":
            scaling = PILImage.BICUBIC
        elif self.scaling == "antialias":
            scaling = PILImage.ANTIALIAS
        else:
            scaling = PILImage.NEAREST

        crop_size = (max_x-min_x, max_y-min_y)

        # modified_imagelayerfillbugfix begin
        fill_needed = ( min(min_x, min_y, max_x, max_y ) < 0 or 
                        max( min_x, max_x ) > abs( self.filebounds[ 2 ] - self.filebounds[ 0 ] ) or
                        max( min_y, max_y ) > abs( self.filebounds[ 3 ] - self.filebounds[ 1 ] ) )
        if fill_needed:
        # modified_imagelayerfillbugfix end
            if self.transparency and self.image.mode in ("L", "RGB"):
                self.image.putalpha(PILImage.new("L", self.image_size, 255));
            sub = self.image.transform(crop_size, PILImage.EXTENT, (min_x, min_y, max_x, max_y))
        else:
            sub = self.image.crop((min_x, min_y, max_x, max_y));
        if crop_size[0] < size[0] and crop_size[1] < size[1] and self.scaling == "antialias":
            scaling = PILImage.BICUBIC
        sub = sub.resize(size, scaling)

        # modified_imagelayerfillcolor begin
        
        if fill_needed and self.fillcolor:
        
            sub2 = PILImage.new( sub.mode, sub.size, self.fillcolor )
            
            # Convert self.filebounds into the pixel coordinate system of the tile
            
            a_res = self.resolutions[ tile.z ]

            fill_x0, fill_y0 = ( -min_x / a_res, -min_y / a_res, )
            fill_x1 = ( self.filebounds[ 2 ] - bounds[ 0 ] ) / self.image_res[ 0 ] / a_res
            fill_y1 = ( bounds[ 3 ] - self.filebounds[ 1 ] ) / self.image_res[ 1 ] / a_res
            
            # Determine the relevant part of sub
            
            rel_x0, rel_x1 = [ max( 0, min( size[ 0 ], x ) ) for x in ( fill_x0, fill_x1 ) ]
            rel_y0, rel_y1 = [ max( 0, min( size[ 1 ], y ) ) for y in ( fill_y0, fill_y1 ) ]

            import math
            
            rel_x0 = math.floor( min( rel_x0, rel_x1 ) )
            rel_x1 = math.ceil( max( rel_x0, rel_x1 ) )
            rel_y0 = math.floor( min( rel_y0, rel_y1 ) )
            rel_y1 = math.ceil( max( rel_y0, rel_y1 ) )
            
            # Copy the relevant part of sub into sub2
            
            rel_bbox = ( rel_x0, rel_y0, rel_x1, rel_y1 )
            
            sub2.paste( sub.crop( rel_bbox ), rel_bbox )
            
            # Update
            
            sub = sub2
            
        # modified_imagelayerfillcolor end
        
        buffer = StringIO.StringIO()
        if self.image.info.has_key('transparency'):
            sub.save(buffer, self.extension, transparency=self.image.info['transparency'])
        else:
            sub.save(buffer, self.extension)

        buffer.seek(0)
        tile.data = buffer.read()
        return tile.data 



      _____________________________________________________________________________ 
Envoyez avec Yahoo! Mail. Une boite mail plus intelligente http://mail.yahoo.fr



More information about the Tilecache mailing list