pngtile/wsgi.py
author Tero Marttila <terom@fixme.fi>
Mon, 25 Jan 2010 18:46:59 +0200
changeset 92 e50ec4217fe6
parent 90 1c317e0628a7
child 93 581cdb831b32
permissions -rw-r--r--
separate non-wsgi render layer to render.png
"""
    Our WSGI web interface, which can serve the JS UI and any .png tiles via HTTP.
"""

from werkzeug import Request, Response, responder
from werkzeug import exceptions

import os.path, os
import pypngtile as pt

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 = {}


### Manipulate request data
def get_req_path (req) :
    """
        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 it is cached
    """

    # 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 request
def handle_dir (req, name, path) :
    """
        Handle request for a directory
    """
    
    prefix = os.path.dirname(req.script_root).rstrip('/')

    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 = os.path.dirname(req.script_root).rstrip('/')

    # viewport
    return Response(render.img_html(prefix, name, image), content_type="text/html")


def handle_img_region (req, image) :
    """
        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), content_type="image/png")

    except ValueError, ex :
        # too large
        raise exceptions.Forbidden(str(ex))


def handle_img_tile (req, image) :
    """
        Handle request for image tile
    """

    # tile
    x = int(req.args['x'])
    y = int(req.args['y'])
    zoom = int(req.args.get('zl', "0"))
        
    # yay render
    return Response(render.img_png_tile(image, x, y, zoom), content_type="image/png")

## Dispatch req to handle_img_*
def handle_img (req, name, path) :
    """
        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)

    elif 'x' in req.args and 'y' in req.args :
        return handle_img_tile(req, image)

    else :
        raise exceptions.BadRequest("Unknown args")



## Dispatch request to handle_*
def handle_req (req) :
    """
        Main request handler
    """
    
    # decode req
    name, path = get_req_path(req)

    # 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)




@responder
def application (env, start_response) :
    """
        Main WSGI entry point
    """

    req = Request(env, start_response)
    
    try :
        return handle_req(req)

    except exceptions.HTTPException, e :
        return e