--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/tile-server Sun Sep 14 17:19:54 2014 +0300
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+"""
+ Development server for pngtile.tile serving.
+"""
+
+import argparse
+import pngtile.tile
+import werkzeug.serving
+
+def main ():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--listen', metavar='ADDR', default='0.0.0.0',
+ help="Listen on address")
+ parser.add_argument('--port', metavar='PORT', type=int, default=8080,
+ help="Listen on port")
+
+ parser.add_argument('--reload', action='store_true',
+ help="Reload")
+ parser.add_argument('--debugger', action='store_true',
+ help="Debugger")
+
+ parser.add_argument('image_root', metavar='PATH',
+ help="Path to images")
+
+ args = parser.parse_args()
+
+ application = pngtile.tile.Application(
+ image_root = args.image_root,
+ )
+
+ werkzeug.serving.run_simple(args.listen, args.port, application,
+ use_reloader = args.reload,
+ use_debugger = args.debugger,
+ )
+
+if __name__ == '__main__':
+ main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/tile.wsgi Sun Sep 14 17:19:54 2014 +0300
@@ -0,0 +1,5 @@
+import pngtile.tile
+
+application = pngtile.tile.Application(
+ image_root = './var',
+)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pngtile/tile.py Sun Sep 14 17:19:54 2014 +0300
@@ -0,0 +1,176 @@
+"""
+ Raw tile handling.
+"""
+
+import os.path
+
+from werkzeug import Request, Response, exceptions
+
+import pypngtile
+
+## Coordinates
+# width of a tile
+TILE_WIDTH = 256
+TILE_HEIGHT = 256
+
+# max. output resolution to allow
+MAX_PIXELS = 1920 * 1200
+
+def scale (val, zoom):
+ """
+ Scale dimension by zoom factor
+
+ zl > 0 -> bigger
+ zl < 0 -> smaller
+ """
+
+ if zoom > 0:
+ return val << zoom
+
+ elif zoom < 0:
+ return val >> -zoom
+
+ else:
+ return val
+
+def scale_center (val, dim, zoom):
+ """
+ Scale value about center by zoom.
+ """
+
+ return scale(val - dim / 2, zoom)
+
+class Application (object):
+ def __init__ (self, image_root):
+ if not os.path.isdir(image_root) :
+ raise Exception("Given image_root does not exist: {image_root}".format(image_root=image_root))
+
+ self.image_root = os.path.abspath(image_root)
+
+ self.image_cache = { }
+
+ def lookup_image (self, url):
+ """
+ Lookup image by request path.
+
+ Returns image_name, image_path.
+ """
+
+ if not os.path.isdir(self.image_root):
+ raise exceptions.InternalServerError("Server image_root has gone missing")
+
+ # path to image
+ name = url.lstrip('/')
+
+ # build absolute path
+ path = os.path.abspath(os.path.join(self.image_root, name))
+
+ # ensure the path points inside the data root
+ if not path.startswith(self.image_root):
+ raise exceptions.NotFound(name)
+
+ return name, path
+
+ def get_image (self, url):
+ """
+ Return Image object.
+ """
+
+ name, path = self.lookup_image(url)
+
+ # get Image object
+ image = self.image_cache.get(path)
+
+ if not image:
+ # open
+ image = pypngtile.Image(path)
+
+ # check
+ if image.status() not in (pypngtile.CACHE_FRESH, pypngtile.CACHE_STALE):
+ raise exceptions.InternalServerError("Image cache not available: {name}".format(name=name))
+
+ # load
+ image.open()
+
+ # cache
+ self.image_cache[path] = image
+
+ return image
+
+ def render_region (self, request, image):
+ """
+ Handle request for an image region
+ """
+
+ width = int(request.args['w'])
+ height = int(request.args['h'])
+ x = int(request.args['x'])
+ y = int(request.args['y'])
+ zoom = int(request.args.get('zoom', "0"))
+
+ # safely limit
+ if width * height > MAX_PIXELS:
+ raise exceptions.BadRequest("Image size: %d * %d > %d" % (width, height, MAX_PIXELS))
+
+ x = scale(x, zoom)
+ y = scale(y, zoom)
+
+ try:
+ return image.tile_mem(width, height, x, y, zoom)
+
+ except pypngtile.Error as error:
+ raise exceptions.BadRequest(str(error))
+
+ def render_tile (self, request, image):
+ """
+ Handle request for image tile
+ """
+
+ width = TILE_WIDTH
+ height = TILE_HEIGHT
+ row = int(request.args['row'])
+ col = int(request.args['col'])
+ zoom = int(request.args.get('zoom', "0"))
+
+ x = scale(row * width, zoom)
+ y = scale(col * height, zoom)
+
+ try:
+ return image.tile_mem(width, height, x, y, zoom)
+
+ except pypngtile.Error as error:
+ raise exceptions.BadRequest(str(error))
+
+ def handle (self, request):
+ """
+ Handle request for an image
+ """
+
+ try:
+ image = self.get_image(request.path)
+ except pypngtile.Error as error:
+ raise exceptions.BadRequest(str(error))
+
+ if 'w' in request.args and 'h' in request.args and 'x' in request.args and 'y' in request.args:
+ png = self.render_region(request, image)
+
+ elif 'row' in request.args and 'col' in request.args:
+ png = self.render_tile(request, image)
+
+ else:
+ raise exceptions.BadRequest("Unknown args")
+
+ return Response(png, content_type='image/png')
+
+ @Request.application
+ def __call__ (self, request):
+ """
+ WSGI entry point.
+ """
+
+ try:
+ return self.handle(request)
+
+ except exceptions.HTTPException as error:
+ return error
+