terom@141: """ terom@141: Image handling. terom@141: """ terom@141: terom@141: terom@141: from werkzeug import Request, Response, exceptions terom@153: from werkzeug.utils import html, redirect terom@168: import werkzeug.urls terom@141: terom@141: import pngtile.tile terom@168: import pngtile.application terom@141: import pypngtile terom@141: terom@141: import json terom@147: import os, os.path terom@147: terom@168: class ImageApplication (pngtile.application.PNGTileApplication): terom@141: terom@141: STYLESHEETS = ( terom@141: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css', terom@141: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css', terom@141: 'http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css', terom@142: '/static/pngtile/map.css', terom@141: ) terom@141: terom@141: SCRIPTS = ( terom@141: 'https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js', terom@141: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js', terom@141: 'http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js', terom@142: '/static/pngtile/map.js', terom@141: ) terom@141: terom@168: def __init__ (self, tiles_server=None, **opts): terom@153: """ terom@153: http://.../ path to tileserver root terom@153: """ terom@153: terom@168: super(ImageApplication, self).__init__(**opts) terom@153: terom@168: self.tiles_server = tiles_server terom@168: terom@168: def tiles_url (self, name, **args): terom@168: return werkzeug.urls.Href(self.tiles_server)(name, **args) terom@168: terom@168: def render_html (self, title, body, stylesheets=None, scripts=None, end=()): terom@168: if stylesheets is None: terom@168: stylesheets = self.STYLESHEETS terom@168: terom@168: if scripts is None: terom@168: scripts = self.SCRIPTS terom@168: terom@168: return html.html(lang='en', *[ terom@168: html.head( terom@168: html.title(title), terom@168: *[ terom@168: html.link(rel='stylesheet', href=href) for href in stylesheets terom@168: ] terom@168: ), terom@168: html.body( terom@168: *(body + tuple( terom@168: html.script(src=src) for src in scripts terom@168: ) + end) terom@168: ), terom@168: ]) terom@168: terom@141: terom@147: def render_dir_breadcrumb (self, name): terom@168: href = werkzeug.urls.Href('/') terom@147: path = [] terom@147: terom@147: yield html.li(html.a(href='/', *[u"Index"])) terom@147: terom@147: if name: terom@147: for part in name.split('/'): terom@147: path.append(part) terom@147: terom@168: yield html.li(html.a(href=href(*path), *[part])) terom@147: terom@176: def render_dir_item (self, name, type): terom@176: if type: terom@176: return html.a(href=name, *[name]) terom@176: else: terom@176: dir = name + '/' terom@176: return html.a(href=dir, *[dir]) terom@176: terom@147: def render_dir (self, request, name, items): terom@147: """ terom@147: request: werkzeug.Request terom@147: name: /.../... url to dir terom@147: items: [...] items in dir terom@147: """ terom@147: terom@147: if name: terom@147: title = name terom@147: else: terom@147: title = "Index" terom@147: terom@147: return self.render_html( terom@147: title = name, terom@147: body = ( terom@147: html.div(class_='container', *[ terom@147: html.h1(title), terom@147: html.div(*[ terom@147: html.ol(class_='breadcrumb', *self.render_dir_breadcrumb(name)), terom@147: ]), terom@147: html.div(class_='list', *[ terom@176: html.ul(class_='list-group', *[ terom@176: html.li(class_='list-group-item', *[ terom@176: self.render_dir_item(name, type) for name, type in items terom@176: ]) for name, type in items terom@176: ]), terom@147: ]), terom@147: ]), terom@147: ), terom@147: ) terom@147: terom@141: def render_image (self, request, image, name): terom@141: """ terom@141: request: werkzeug.Request terom@141: image: pypngtile.Image terom@153: name: request path for .../.../*.png image terom@141: """ terom@141: terom@141: image_info = image.info() terom@141: terom@142: map_config = dict( terom@168: tiles_url = self.tiles_url(name), terom@172: tiles_mtime = image_info['cache_mtime'], terom@141: terom@172: tile_url = '{tiles_url}?t={tiles_mtime}&x={x}&y={y}&zoom={z}', terom@141: tile_size = pngtile.tile.TILE_SIZE, terom@141: tile_zoom = pngtile.tile.MAX_ZOOM, terom@141: terom@172: # do NOT include a mtime in these urls.. we should always revalidate them terom@168: image_url = '{tiles_url}?w={w}&h={h}&x={x}&y={y}&zoom={z}', terom@141: image_width = image_info['img_width'], terom@141: image_height = image_info['img_height'], terom@141: ) terom@141: terom@141: return self.render_html( terom@141: title = name, terom@141: body = ( terom@141: html.div(id='wrapper', *[ terom@141: html.div(id='map') terom@141: ]), terom@141: ), terom@141: end = ( terom@142: html.script("""$(function() {{ map_init({map_config}); }});""".format(map_config=json.dumps(map_config))), terom@141: ), terom@141: ) terom@141: terom@147: def handle_dir (self, request, name, path): terom@141: """ terom@147: Generate response for directory listing. terom@141: """ terom@147: terom@153: if not request.path.endswith('/'): terom@153: # we generate HTML with relative links terom@153: return redirect(request.path + '/') terom@153: terom@168: items = sorted(self.list(request.path)) terom@147: terom@147: html = self.render_dir(request, name, items) terom@147: terom@147: return Response(html, content_type="text/html") terom@147: terom@147: def handle_image (self, request, name, path): terom@147: """ terom@147: Generate Response for image request. terom@147: """ terom@153: terom@153: # backwards-compat redirect from frontend -> tile-server terom@153: if all(attr in request.args for attr in ('cx', 'cy', 'w', 'h', 'zl')): terom@168: return redirect(self.tiles_url(name, terom@153: w = request.args['w'], terom@153: h = request.args['h'], terom@153: x = request.args['cx'], terom@153: y = request.args['cy'], terom@153: zoom = request.args['zl'], terom@153: )) terom@153: terom@168: image, name = self.open(request.path) terom@141: terom@141: html = self.render_image(request, image, name) terom@141: terom@141: return Response(html, content_type="text/html") terom@141: terom@147: def handle (self, request): terom@147: """ terom@147: Handle request for an image terom@147: """ terom@141: terom@168: name, path, type = self.lookup(request.path) terom@147: terom@147: # determine handler terom@164: if not type: terom@147: return self.handle_dir(request, name, path) terom@147: terom@164: else: terom@147: return self.handle_image(request, name, path) terom@147: terom@168: @Request.application terom@168: def __call__ (self, request): terom@168: """ terom@168: WSGI entry point. terom@168: """ terom@168: terom@168: try: terom@168: return self.handle(request) terom@168: terom@168: except exceptions.HTTPException as error: terom@168: return error terom@168: