# HG changeset patch # User Tero Marttila # Date 1264461965 -7200 # Node ID 1a6a6957197d4362312932791f553395dae2191c # Parent a4d1e046ed3e6835189ce7bfb5803f27100584f9 basic caching behaviour, not use yet diff -r a4d1e046ed3e -r 1a6a6957197d bin/dev-server --- a/bin/dev-server Mon Jan 25 22:11:10 2010 +0200 +++ b/bin/dev-server Tue Jan 26 01:26:05 2010 +0200 @@ -6,18 +6,24 @@ import pngtile.wsgi -# dispatch on URL -app = werkzeug.DispatcherMiddleware(pngtile.wsgi.application, { - '/static': werkzeug.SharedDataMiddleware(NotFound(), { - '/': 'static', - }), -}) +def main (host='0.0.0.0', port=8000) : + # original app + app = pngtile.wsgi.WSGIApplication() -def main (host='0.0.0.0', port=8000) : + # dispatch on URL + # XXX: just replace with SharedDataMiddleware.. + app = werkzeug.DispatcherMiddleware(app, { + '/static': werkzeug.SharedDataMiddleware(NotFound(), { + '/': 'static', + }), + }) + + # http server httpd = wsgiref.simple_server.make_server(host, port, app) print "Listening on %s:%d" % (host, port) - + + # go httpd.serve_forever() if __name__ == '__main__' : diff -r a4d1e046ed3e -r 1a6a6957197d pngtile/handlers.py --- a/pngtile/handlers.py Mon Jan 25 22:11:10 2010 +0200 +++ b/pngtile/handlers.py Tue Jan 26 01:26:05 2010 +0200 @@ -11,7 +11,6 @@ # only open each image once IMAGE_CACHE = {} - ### Parse request data def get_path (req_path) : """ @@ -84,7 +83,7 @@ return Response(render.img_html(prefix, name, image), content_type="text/html") -def handle_img_region (req, image) : +def handle_img_region (req, image, cache) : """ Handle request for an image region """ @@ -98,14 +97,14 @@ try : # yay full render - return Response(render.img_png_region(image, cx, cy, zoom, width, height), content_type="image/png") + return Response(render.img_png_region(image, cx, cy, zoom, width, height, cache), content_type="image/png") except ValueError, ex : # too large raise exceptions.Forbidden(str(ex)) -def handle_img_tile (req, image) : +def handle_img_tile (req, image, cache) : """ Handle request for image tile """ @@ -114,12 +113,14 @@ x = int(req.args['x']) y = int(req.args['y']) zoom = int(req.args.get('zl', "0")) + + # cache? # yay render - return Response(render.img_png_tile(image, x, y, zoom), content_type="image/png") + return Response(render.img_png_tile(image, x, y, zoom, cache), content_type="image/png") ## Dispatch req to handle_img_* -def handle_img (req, name, path) : +def handle_img (req, name, path, cache) : """ Handle request for an image """ @@ -132,10 +133,10 @@ return handle_img_viewport(req, image, name) elif 'w' in req.args and 'h' in req.args and 'cx' in req.args and 'cy' in req.args : - return handle_img_region(req, image) + return handle_img_region(req, image, cache) elif 'x' in req.args and 'y' in req.args : - return handle_img_tile(req, image) + return handle_img_tile(req, image, cache) else : raise exceptions.BadRequest("Unknown args") @@ -143,7 +144,7 @@ ## Dispatch request to handle_* -def handle_req (req) : +def handle_req (req, cache) : """ Main request handler """ @@ -166,6 +167,6 @@ else : # image - return handle_img(req, name, path) + return handle_img(req, name, path, cache) diff -r a4d1e046ed3e -r 1a6a6957197d pngtile/render.py --- a/pngtile/render.py Mon Jan 25 22:11:10 2010 +0200 +++ b/pngtile/render.py Tue Jan 26 01:26:05 2010 +0200 @@ -52,20 +52,6 @@ if ext == '.png' and os.path.exists(base + '.cache') : yield item -def scale_by_zoom (val, zoom) : - """ - Scale coordinates by zoom factor - """ - - if zoom > 0 : - return val << zoom - - elif zoom > 0 : - return val >> -zoom - - else : - return val - ### Render HTML data def dir_html (prefix, name, path) : @@ -157,19 +143,90 @@ img_height = img_height, ) + +# threshold to cache images on - only images with a source data region *larger* than this are cached +CACHE_THRESHOLD = 512 * 512 + +def scale_by_zoom (val, zoom) : + """ + Scale dimension by zoom factor + """ + + if zoom > 0 : + return val << zoom + + elif zoom > 0 : + return val >> -zoom + + else : + return val + +### Image caching +def check_cache_threshold (width, height, zl) : + """ + Checks if a tile with the given dimensions should be cached + """ + + return (scale_by_zoom(width, -zl) * scale_by_zoom(height, -zl)) > CACHE_THRESHOLD + +def render_raw (image, width, height, x, y, zl) : + """ + Render and return tile + """ + + return image.tile_mem( + width, height, + x, y, zl + ) + +def render_cache (cache, image, width, height, x, y, zl) : + """ + Perform a cached render of the given tile + """ + + if cache : + # cache key + # XXX: need a better id for the image.. + key = "tl_%d:%d_%d:%d:%d_%s" % (x, y, width, height, zl, id(image)) + + # lookup + data = cache.get(key) + + else : + # oops, no cache + data = None + + if not data : + # cache miss, render + data = render_raw(image, width, height, x, y, zl) + + if cache : + # store + cache.add(key, data) + + # ok + return data + ### Render PNG Data -def img_png_tile (image, x, y, zoom) : +def img_png_tile (image, x, y, zoom, cache) : """ Render given tile, returning PNG data """ - return image.tile_mem( - TILE_WIDTH, TILE_HEIGHT, - scale_by_zoom(x, -zoom), scale_by_zoom(y, -zoom), - zoom - ) + # remap coordinates by zoom + x = scale_by_zoom(x, -zoom) + y = scale_by_zoom(y, -zoom) -def img_png_region (image, cx, cy, zoom, width, height) : + # do we want to cache this? + if check_cache_threshold(TILE_WIDTH, TILE_HEIGHT, zoom) : + # go via the cache + return render_cache(cache, image, TILE_WIDTH, TILE_HEIGHT, x, y, zoom) + + else : + # just go raw + return render_raw(image, TILE_WIDTH, TILE_HEIGHT, x, y, zoom) + +def img_png_region (image, cx, cy, zoom, width, height, cache) : """ Render arbitrary tile, returning PNG data """ @@ -180,10 +237,7 @@ # safely limit if width * height > MAX_PIXELS : raise ValueError("Image size: %d * %d > %d" % (width, height, MAX_PIXELS)) + + # these images are always cached + return render_cache(cache, image, width, height, x, y, zoom) - return image.tile_mem( - width, height, - x, y, - zoom - ) - diff -r a4d1e046ed3e -r 1a6a6957197d pngtile/wsgi.py --- a/pngtile/wsgi.py Mon Jan 25 22:11:10 2010 +0200 +++ b/pngtile/wsgi.py Tue Jan 26 01:26:05 2010 +0200 @@ -8,19 +8,31 @@ from pngtile import handlers -@responder -def application (env, start_response) : +class WSGIApplication (object) : """ - Main WSGI entry point. - - This is wrapped with werkzeug, so we can return a Response object + Simple WSGI application invoking the werkzeug handlers """ - req = Request(env, start_response) - - try : - return handlers.handle_req(req) + def __init__ (self, cache=None) : + """ + Use given cache if any + """ - except exceptions.HTTPException, e : - return e + self.cache = cache + @responder + def __call__ (self, env, start_response) : + """ + Main WSGI entry point. + + This is wrapped with werkzeug, so we can return a Response object + """ + + req = Request(env, start_response) + + try : + return handlers.handle_req(req, self.cache) + + except exceptions.HTTPException, e : + return e +