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)