pngtile/tile.py
changeset 135 e99dd75afa15
child 138 59d61da2b64f
equal deleted inserted replaced
134:08a0056f6175 135:e99dd75afa15
       
     1 """
       
     2     Raw tile handling.
       
     3 """
       
     4 
       
     5 import os.path
       
     6 
       
     7 from werkzeug import Request, Response, exceptions
       
     8 
       
     9 import pypngtile
       
    10 
       
    11 ## Coordinates
       
    12 # width of a tile
       
    13 TILE_WIDTH = 256
       
    14 TILE_HEIGHT = 256
       
    15 
       
    16 # max. output resolution to allow
       
    17 MAX_PIXELS = 1920 * 1200
       
    18 
       
    19 def scale (val, zoom):
       
    20     """
       
    21         Scale dimension by zoom factor
       
    22 
       
    23         zl > 0 -> bigger
       
    24         zl < 0 -> smaller
       
    25     """
       
    26 
       
    27     if zoom > 0:
       
    28         return val << zoom
       
    29 
       
    30     elif zoom < 0:
       
    31         return val >> -zoom
       
    32 
       
    33     else:
       
    34         return val
       
    35 
       
    36 def scale_center (val, dim, zoom):
       
    37     """
       
    38         Scale value about center by zoom.
       
    39     """
       
    40 
       
    41     return scale(val - dim / 2, zoom)
       
    42 
       
    43 class Application (object):
       
    44     def __init__ (self, image_root):
       
    45         if not os.path.isdir(image_root) :
       
    46             raise Exception("Given image_root does not exist: {image_root}".format(image_root=image_root))
       
    47 
       
    48         self.image_root = os.path.abspath(image_root)
       
    49 
       
    50         self.image_cache = { }
       
    51 
       
    52     def lookup_image (self, url):
       
    53         """
       
    54             Lookup image by request path.
       
    55 
       
    56             Returns image_name, image_path.
       
    57         """
       
    58 
       
    59         if not os.path.isdir(self.image_root):
       
    60             raise exceptions.InternalServerError("Server image_root has gone missing")
       
    61     
       
    62         # path to image
       
    63         name = url.lstrip('/')
       
    64         
       
    65         # build absolute path
       
    66         path = os.path.abspath(os.path.join(self.image_root, name))
       
    67 
       
    68         # ensure the path points inside the data root
       
    69         if not path.startswith(self.image_root):
       
    70             raise exceptions.NotFound(name)
       
    71 
       
    72         return name, path
       
    73 
       
    74     def get_image (self, url):
       
    75         """
       
    76             Return Image object.
       
    77         """
       
    78 
       
    79         name, path = self.lookup_image(url)
       
    80 
       
    81         # get Image object
       
    82         image = self.image_cache.get(path)
       
    83 
       
    84         if not image:
       
    85             # open
       
    86             image = pypngtile.Image(path)
       
    87 
       
    88             # check
       
    89             if image.status() not in (pypngtile.CACHE_FRESH, pypngtile.CACHE_STALE):
       
    90                 raise exceptions.InternalServerError("Image cache not available: {name}".format(name=name))
       
    91 
       
    92             # load
       
    93             image.open()
       
    94 
       
    95             # cache
       
    96             self.image_cache[path] = image
       
    97         
       
    98         return image
       
    99 
       
   100     def render_region (self, request, image):
       
   101         """
       
   102             Handle request for an image region
       
   103         """
       
   104 
       
   105         width = int(request.args['w'])
       
   106         height = int(request.args['h'])
       
   107         x = int(request.args['x'])
       
   108         y = int(request.args['y'])
       
   109         zoom = int(request.args.get('zoom', "0"))
       
   110 
       
   111         # safely limit
       
   112         if width * height > MAX_PIXELS:
       
   113             raise exceptions.BadRequest("Image size: %d * %d > %d" % (width, height, MAX_PIXELS))
       
   114 
       
   115         x = scale(x, zoom)
       
   116         y = scale(y, zoom)
       
   117         
       
   118         try:
       
   119             return image.tile_mem(width, height, x, y, zoom)
       
   120 
       
   121         except pypngtile.Error as error:
       
   122             raise exceptions.BadRequest(str(error))
       
   123         
       
   124     def render_tile (self, request, image):
       
   125         """
       
   126             Handle request for image tile
       
   127         """
       
   128 
       
   129         width = TILE_WIDTH
       
   130         height = TILE_HEIGHT
       
   131         row = int(request.args['row'])
       
   132         col = int(request.args['col'])
       
   133         zoom = int(request.args.get('zoom', "0"))
       
   134 
       
   135         x = scale(row * width, zoom)
       
   136         y = scale(col * height, zoom)
       
   137 
       
   138         try:
       
   139             return image.tile_mem(width, height, x, y, zoom)
       
   140 
       
   141         except pypngtile.Error as error:
       
   142             raise exceptions.BadRequest(str(error))
       
   143 
       
   144     def handle (self, request):
       
   145         """
       
   146             Handle request for an image
       
   147         """
       
   148         
       
   149         try:
       
   150             image = self.get_image(request.path)
       
   151         except pypngtile.Error as error:
       
   152             raise exceptions.BadRequest(str(error))
       
   153 
       
   154         if 'w' in request.args and 'h' in request.args and 'x' in request.args and 'y' in request.args:
       
   155             png = self.render_region(request, image)
       
   156 
       
   157         elif 'row' in request.args and 'col' in request.args:
       
   158             png = self.render_tile(request, image)
       
   159 
       
   160         else:
       
   161             raise exceptions.BadRequest("Unknown args")
       
   162 
       
   163         return Response(png, content_type='image/png')
       
   164 
       
   165     @Request.application
       
   166     def __call__ (self, request):
       
   167         """
       
   168             WSGI entry point.
       
   169         """
       
   170 
       
   171         try:
       
   172             return self.handle(request)
       
   173 
       
   174         except exceptions.HTTPException as error:
       
   175             return error
       
   176