pngtile/image.py
changeset 168 260aa4a05e82
parent 164 e1e0c8099c8b
child 172 73380dd6a816
equal deleted inserted replaced
167:16b600e927fe 168:260aa4a05e82
     3 """
     3 """
     4 
     4 
     5 
     5 
     6 from werkzeug import Request, Response, exceptions
     6 from werkzeug import Request, Response, exceptions
     7 from werkzeug.utils import html, redirect
     7 from werkzeug.utils import html, redirect
       
     8 import werkzeug.urls
     8 
     9 
     9 import pngtile.tile
    10 import pngtile.tile
    10 from pngtile.application import url, BaseApplication
    11 import pngtile.application
    11 import pypngtile
    12 import pypngtile
    12 
    13 
    13 import json
    14 import json
    14 import os, os.path
    15 import os, os.path
    15 
    16 
    16 def dir_list (root):
    17 class ImageApplication (pngtile.application.PNGTileApplication):
    17     """
       
    18         Yield a series of directory items to show for the given dir
       
    19     """
       
    20 
       
    21     for name in os.listdir(root):
       
    22         path = os.path.join(root, name)
       
    23 
       
    24         # skip dotfiles
       
    25         if name.startswith('.'):
       
    26             continue
       
    27         
       
    28         # show dirs
       
    29         if os.path.isdir(path):
       
    30             if not os.access(path, os.R_OK):
       
    31                 # skip inaccessible dirs
       
    32                 continue
       
    33 
       
    34             yield name + '/'
       
    35 
       
    36         # examine ext
       
    37         if '.' in name:
       
    38             name_base, name_type = name.rsplit('.', 1)
       
    39         else:
       
    40             name_base = name
       
    41             name_type = None
       
    42 
       
    43         # show .png files with a .cache file
       
    44         if name_type in ImageApplication.IMAGE_TYPES and os.path.exists(os.path.join(root, name_base + '.cache')):
       
    45             yield name
       
    46 
       
    47 class ImageApplication (BaseApplication):
       
    48 
    18 
    49     STYLESHEETS = (
    19     STYLESHEETS = (
    50         'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css',
    20         'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css',
    51         'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css',
    21         'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css',
    52         'http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css',
    22         'http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css',
    58         'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js',
    28         'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js',
    59         'http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js',
    29         'http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js',
    60         '/static/pngtile/map.js',
    30         '/static/pngtile/map.js',
    61     )
    31     )
    62 
    32 
    63     def __init__ (self, tile_server=None, **opts):
    33     def __init__ (self, tiles_server=None, **opts):
    64         """
    34         """
    65             http://.../ path to tileserver root
    35             http://.../ path to tileserver root
    66         """
    36         """
    67 
    37 
    68         BaseApplication.__init__(self, **opts)
    38         super(ImageApplication, self).__init__(**opts)
    69                 
    39                 
    70         self.tile_server = tile_server
    40         self.tiles_server = tiles_server
       
    41 
       
    42     def tiles_url (self, name, **args):
       
    43         return werkzeug.urls.Href(self.tiles_server)(name, **args)
       
    44 
       
    45     def render_html (self, title, body, stylesheets=None, scripts=None, end=()):
       
    46         if stylesheets is None:
       
    47             stylesheets = self.STYLESHEETS
       
    48 
       
    49         if scripts is None:
       
    50             scripts = self.SCRIPTS
       
    51 
       
    52         return html.html(lang='en', *[
       
    53             html.head(
       
    54                 html.title(title),
       
    55                 *[
       
    56                     html.link(rel='stylesheet', href=href) for href in stylesheets
       
    57                 ]
       
    58             ),
       
    59             html.body(
       
    60                 *(body + tuple(
       
    61                     html.script(src=src) for src in scripts
       
    62                 ) + end)
       
    63             ),
       
    64         ])
       
    65 
    71 
    66 
    72     def render_dir_breadcrumb (self, name):
    67     def render_dir_breadcrumb (self, name):
       
    68         href = werkzeug.urls.Href('/')
    73         path = []
    69         path = []
    74 
    70 
    75         yield html.li(html.a(href='/', *[u"Index"]))
    71         yield html.li(html.a(href='/', *[u"Index"]))
    76         
    72         
    77         if name:
    73         if name:
    78             for part in name.split('/'):
    74             for part in name.split('/'):
    79                 path.append(part)
    75                 path.append(part)
    80 
    76 
    81                 yield html.li(html.a(href=url('/', *path), *[part]))
    77                 yield html.li(html.a(href=href(*path), *[part]))
    82 
    78 
    83     def render_dir (self, request, name, items):
    79     def render_dir (self, request, name, items):
    84         """
    80         """
    85             request:    werkzeug.Request
    81             request:    werkzeug.Request
    86             name:       /.../... url to dir
    82             name:       /.../... url to dir
   118         """
   114         """
   119 
   115 
   120         image_info = image.info()
   116         image_info = image.info()
   121 
   117 
   122         map_config = dict(
   118         map_config = dict(
   123             tile_url        = '{server}/{name}?x={x}&y={y}&zoom={z}',
   119             tiles_url       = self.tiles_url(name),
   124             tile_server     = self.tile_server.rstrip('/'),
   120 
   125             tile_name       = name,
   121             tile_url        = '{tiles_url}?x={x}&y={y}&zoom={z}',
   126 
       
   127             tile_size       = pngtile.tile.TILE_SIZE,
   122             tile_size       = pngtile.tile.TILE_SIZE,
   128             tile_zoom       = pngtile.tile.MAX_ZOOM,
   123             tile_zoom       = pngtile.tile.MAX_ZOOM,
   129             
   124             
   130             image_url       = '{server}/{name}?w={w}&h={h}&x={x}&y={y}&zoom={z}',
   125             image_url       = '{tiles_url}?w={w}&h={h}&x={x}&y={y}&zoom={z}',
   131             image_server    = self.tile_server.rstrip('/'),
       
   132             image_name      = name,
       
   133             image_width     = image_info['img_width'],
   126             image_width     = image_info['img_width'],
   134             image_height    = image_info['img_height'],
   127             image_height    = image_info['img_height'],
   135         )
   128         )
   136 
   129 
   137         return self.render_html(
   130         return self.render_html(
   153 
   146 
   154         if not request.path.endswith('/'):
   147         if not request.path.endswith('/'):
   155             # we generate HTML with relative links
   148             # we generate HTML with relative links
   156             return redirect(request.path + '/')
   149             return redirect(request.path + '/')
   157 
   150 
   158         items = sorted(dir_list(path))
   151         items = sorted(self.list(request.path))
   159 
   152 
   160         html = self.render_dir(request, name, items)
   153         html = self.render_dir(request, name, items)
   161 
   154 
   162         return Response(html, content_type="text/html")
   155         return Response(html, content_type="text/html")
   163 
   156 
   166             Generate Response for image request.
   159             Generate Response for image request.
   167         """
   160         """
   168 
   161 
   169         # backwards-compat redirect from frontend -> tile-server
   162         # backwards-compat redirect from frontend -> tile-server
   170         if all(attr in request.args for attr in ('cx', 'cy', 'w', 'h', 'zl')):
   163         if all(attr in request.args for attr in ('cx', 'cy', 'w', 'h', 'zl')):
   171             return redirect(url(self.tile_server, name,
   164             return redirect(self.tiles_url(name,
   172                 w       = request.args['w'],
   165                 w       = request.args['w'],
   173                 h       = request.args['h'],
   166                 h       = request.args['h'],
   174                 x       = request.args['cx'],
   167                 x       = request.args['cx'],
   175                 y       = request.args['cy'],
   168                 y       = request.args['cy'],
   176                 zoom    = request.args['zl'],
   169                 zoom    = request.args['zl'],
   177             ))
   170             ))
   178 
   171 
   179         try:
   172         image, name = self.open(request.path)
   180             image, name = self.get_image(request.path)
       
   181         except pypngtile.Error as error:
       
   182             raise exceptions.BadRequest(str(error))
       
   183 
   173 
   184         html = self.render_image(request, image, name)
   174         html = self.render_image(request, image, name)
   185 
   175 
   186         return Response(html, content_type="text/html")
   176         return Response(html, content_type="text/html")
   187 
   177 
   188     def handle (self, request):
   178     def handle (self, request):
   189         """
   179         """
   190             Handle request for an image
   180             Handle request for an image
   191         """
   181         """
   192 
   182 
   193         name, path, type = self.lookup_path(request.path)
   183         name, path, type = self.lookup(request.path)
   194         
   184         
   195         # determine handler
   185         # determine handler
   196         if not type:
   186         if not type:
   197             return self.handle_dir(request, name, path)
   187             return self.handle_dir(request, name, path)
   198         
   188         
   199         else:
   189         else:
   200             return self.handle_image(request, name, path)
   190             return self.handle_image(request, name, path)
   201 
   191 
       
   192     @Request.application
       
   193     def __call__ (self, request):
       
   194         """
       
   195             WSGI entry point.
       
   196         """
       
   197 
       
   198         try:
       
   199             return self.handle(request)
       
   200 
       
   201         except exceptions.HTTPException as error:
       
   202             return error
       
   203