pngtile/handlers.py
author Tero Marttila <terom@fixme.fi>
Sat, 10 Apr 2010 22:30:00 +0300
branchunscaled-coordinates
changeset 128 66c95c2d212c
parent 126 2e0f7cbe528f
permissions -rw-r--r--
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)