pngtile/image.py
author Tero Marttila <terom@paivola.fi>
Mon, 15 Sep 2014 01:23:01 +0300
changeset 147 77330e43c855
parent 145 51908b0cc3a1
child 153 55f3d3bc95d3
permissions -rw-r--r--
pngtile.image: handle dir indexes
"""
    Image handling.
"""


from werkzeug import Request, Response, exceptions
from werkzeug.utils import html

import pngtile.tile
from pngtile.application import BaseApplication
import pypngtile

import json
import os, os.path

def dir_url (prefix, name, item):
    """
        Join together an absolute URL prefix, an optional directory name, and a directory item
    """

    url = prefix

    if name:
        url += '/' + name
    
    url += '/' + item

    return url

def dir_list (root):
    """
        Yield a series of directory items to show for the given dir
    """

    for name in os.listdir(root):
        path = os.path.join(root, name)

        # skip dotfiles
        if name.startswith('.'):
            continue
        
        # show dirs
        if os.path.isdir(path):
            yield name

        # examine ext
        if '.' in name:
            name_base, name_type = name.rsplit('.', 1)
        else:
            name_base = name
            name_type = None

        # show .png files with a .cache file
        if name_type in ImageApplication.IMAGE_TYPES and os.path.exists(os.path.join(root, name_base + '.cache')):
            yield name

class ImageApplication (BaseApplication):

    STYLESHEETS = (
        'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css',
        'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css',
        'http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css',
        '/static/pngtile/map.css',
    )

    SCRIPTS = (
        'https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js',
        'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js',
        'http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js',
        '/static/pngtile/map.js',
    )

    def __init__ (self, **opts):
        BaseApplication.__init__(self, **opts)

    def render_dir_breadcrumb (self, name):
        path = []

        yield html.li(html.a(href='/', *[u"Index"]))
        
        if name:
            for part in name.split('/'):
                path.append(part)

                yield html.li(html.a(href='/'.join(path), *[part]))

    def render_dir (self, request, name, items):
        """
            request:    werkzeug.Request
            name:       /.../... url to dir
            items:      [...] items in dir
        """
        
        if name:
            title = name
        else:
            title = "Index"

        return self.render_html(
            title       = name,
            body        = (
                html.div(class_='container', *[
                    html.h1(title),
                    html.div(*[
                        html.ol(class_='breadcrumb', *self.render_dir_breadcrumb(name)),
                    ]),
                    html.div(class_='list', *[
                        html.ul(class_='list-group', *[html.li(class_='list-group-item', *[
                                html.a(href=dir_url('', name, item), *[item])
                            ]) for item in items]
                        ),
                    ]),
                ]),
            ),
        )

    def render_image (self, request, image, name):
        """
            request:    werkzeug.Request
            image:      pypngtile.Image
            name:       request path for .png image
        """

        image_info = image.info()

        map_config = dict(
            tile_url        = 'http://zovoweix.qmsk.net:8080/{name}?x={x}&y={y}&zoom={z}',
            tile_name       = name,

            tile_size       = pngtile.tile.TILE_SIZE,
            tile_zoom       = pngtile.tile.MAX_ZOOM,
            
            image_url       = 'http://zovoweix.qmsk.net:8080/{name}?w={w}&h={h}&x={x}&y={y}&zoom={z}',
            image_name      = name,
            image_width     = image_info['img_width'],
            image_height    = image_info['img_height'],
        )

        return self.render_html(
            title       = name,
            body        = (
                html.div(id='wrapper', *[
                    html.div(id='map')
                ]),
            ),
            end         = (
                html.script("""$(function() {{ map_init({map_config}); }});""".format(map_config=json.dumps(map_config))),
            ),
        )

    def handle_dir (self, request, name, path):
        """
            Generate response for directory listing.
        """

        items = sorted(dir_list(path))

        html = self.render_dir(request, name, items)

        return Response(html, content_type="text/html")

    def handle_image (self, request, name, path):
        """
            Generate Response for image request.
        """
        
        try:
            image, name = self.get_image(request.path)
        except pypngtile.Error as error:
            raise exceptions.BadRequest(str(error))

        html = self.render_image(request, image, name)

        return Response(html, content_type="text/html")

    def handle (self, request):
        """
            Handle request for an image
        """

        name, path = self.lookup_path(request.path)
        
        # determine type
        if '/' in name:
            _, name_base = name.rsplit('/', 1)
        else:
            name_base = name

        if '.' in name_base:
            name_base, name_type = name_base.rsplit('.', 1)
        else:
            name_type = None

        # determine handler
        if os.path.isdir(path):
            return self.handle_dir(request, name, path)
        
        elif name_type and name_type in self.IMAGE_TYPES:
            return self.handle_image(request, name, path)

        else:
            raise exceptions.NotFound(path)