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 |