[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