--- 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__' :
--- 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)
--- 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
- )
-
--- 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
+