partial implementation of unscaled coordinates in URLs, but broken for url hashes and view images
import os, os.path
import pypngtile as pt
from werkzeug import Response, exceptions
from pngtile import render
# path to images
DATA_ROOT = os.environ.get("PNGTILE_DATA_PATH") or os.path.abspath('data/')
# only open each image once
IMAGE_CACHE = {}
### Parse request data
def get_path (req_path) :
"""
Returns the name and path requested
"""
# check DATA_ROOT exists..
if not os.path.isdir(DATA_ROOT) :
raise exceptions.InternalServerError("Missing DATA_ROOT")
# path to image
image_name = req_path.lstrip('/')
# build absolute path
image_path = os.path.abspath(os.path.join(DATA_ROOT, image_name))
# ensure the path points inside the data root
if not image_path.startswith(DATA_ROOT) :
raise exceptions.NotFound(image_name)
return image_name, image_path
def get_image (name, path) :
"""
Gets an Image object from the cache, ensuring that the cached is available
"""
# get Image object
if path in IMAGE_CACHE :
# get from cache
image = IMAGE_CACHE[path]
else :
# open
image = pt.Image(path)
# check
if image.status() not in (pt.CACHE_FRESH, pt.CACHE_STALE) :
raise exceptions.InternalServerError("Image cache not available: %s" % name)
# load
image.open()
# cache
IMAGE_CACHE[path] = image
return image
### Handle werkzeug.Request objects -> werkzeug.Response
def handle_dir (req, name, path) :
"""
Handle request for a directory
"""
prefix = req.script_root
return Response(render.dir_html(prefix, name, path), content_type="text/html")
def handle_img_viewport (req, image, name) :
"""
Handle request for image viewport
"""
prefix = req.script_root
# viewport
return Response(render.img_html(prefix, name, image), content_type="text/html")
def handle_img_region (req, image, cache) :
"""
Handle request for an image region
"""
# specific image
width = int(req.args['w'])
height = int(req.args['h'])
cx = int(req.args['cx'])
cy = int(req.args['cy'])
zoom = int(req.args.get('zl', "0"))
try :
# yay full render
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, cache) :
"""
Handle request for image tile
"""
# tile
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, cache), content_type="image/png")
## Dispatch req to handle_img_*
def handle_img (req, name, path, cache) :
"""
Handle request for an image
"""
# get image object
image = get_image(name, path)
# what view?
if not req.args :
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, cache)
elif 'x' in req.args and 'y' in req.args :
return handle_img_tile(req, image, cache)
else :
raise exceptions.BadRequest("Unknown args")
## Dispatch request to handle_*
def handle_req (req, cache) :
"""
Main request handler
"""
# decode req
name, path = get_path(req.path)
# determine dir/image
if os.path.isdir(path) :
# directory
return handle_dir(req, name, path)
elif not os.path.exists(path) :
# no such file
raise exceptions.NotFound(name)
elif not name or not name.endswith('.png') :
# invalid file
raise exceptions.BadRequest("Not a PNG file")
else :
# image
return handle_img(req, name, path, cache)