author | Tero Marttila <terom@fixme.fi> |
Mon, 25 Jan 2010 18:46:59 +0200 | |
changeset 92 | e50ec4217fe6 |
parent 90 | 1c317e0628a7 |
child 93 | 581cdb831b32 |
permissions | -rw-r--r-- |
30 | 1 |
""" |
2 |
Our WSGI web interface, which can serve the JS UI and any .png tiles via HTTP. |
|
3 |
""" |
|
4 |
||
5 |
from werkzeug import Request, Response, responder |
|
6 |
from werkzeug import exceptions |
|
92
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
7 |
|
42
a5bca7b0cd8a
get DATA_ROOT from os.environ, fix use of prefix for dir view
Tero Marttila <terom@fixme.fi>
parents:
40
diff
changeset
|
8 |
import os.path, os |
30 | 9 |
import pypngtile as pt |
10 |
||
92
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
11 |
from pngtile import render |
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
12 |
|
89 | 13 |
# path to images |
42
a5bca7b0cd8a
get DATA_ROOT from os.environ, fix use of prefix for dir view
Tero Marttila <terom@fixme.fi>
parents:
40
diff
changeset
|
14 |
DATA_ROOT = os.environ.get("PNGTILE_DATA_PATH") or os.path.abspath('data/') |
30 | 15 |
|
89 | 16 |
# only open each image once |
30 | 17 |
IMAGE_CACHE = {} |
18 |
||
89 | 19 |
|
20 |
### Manipulate request data |
|
21 |
def get_req_path (req) : |
|
22 |
""" |
|
23 |
Returns the name and path requested |
|
24 |
""" |
|
90 | 25 |
|
26 |
# check DATA_ROOT exists.. |
|
27 |
if not os.path.isdir(DATA_ROOT) : |
|
28 |
raise exceptions.InternalServerError("Missing DATA_ROOT") |
|
89 | 29 |
|
30 | 30 |
# path to image |
31
7eec7486a0af
dir index view, report if image not cached
Tero Marttila <terom@fixme.fi>
parents:
30
diff
changeset
|
31 |
image_name = req.path.lstrip('/') |
30 | 32 |
|
33 |
# build absolute path |
|
31
7eec7486a0af
dir index view, report if image not cached
Tero Marttila <terom@fixme.fi>
parents:
30
diff
changeset
|
34 |
image_path = os.path.abspath(os.path.join(DATA_ROOT, image_name)) |
42
a5bca7b0cd8a
get DATA_ROOT from os.environ, fix use of prefix for dir view
Tero Marttila <terom@fixme.fi>
parents:
40
diff
changeset
|
35 |
|
30 | 36 |
# ensure the path points inside the data root |
37 |
if not image_path.startswith(DATA_ROOT) : |
|
31
7eec7486a0af
dir index view, report if image not cached
Tero Marttila <terom@fixme.fi>
parents:
30
diff
changeset
|
38 |
raise exceptions.NotFound(image_name) |
7eec7486a0af
dir index view, report if image not cached
Tero Marttila <terom@fixme.fi>
parents:
30
diff
changeset
|
39 |
|
89 | 40 |
return image_name, image_path |
31
7eec7486a0af
dir index view, report if image not cached
Tero Marttila <terom@fixme.fi>
parents:
30
diff
changeset
|
41 |
|
89 | 42 |
def get_image (name, path) : |
43 |
""" |
|
44 |
Gets an Image object from the cache, ensuring that it is cached |
|
45 |
""" |
|
31
7eec7486a0af
dir index view, report if image not cached
Tero Marttila <terom@fixme.fi>
parents:
30
diff
changeset
|
46 |
|
30 | 47 |
# get Image object |
89 | 48 |
if path in IMAGE_CACHE : |
30 | 49 |
# get from cache |
89 | 50 |
image = IMAGE_CACHE[path] |
30 | 51 |
|
52 |
else : |
|
89 | 53 |
# open |
54 |
image = pt.Image(path) |
|
55 |
||
56 |
# check |
|
57 |
if image.status() not in (pt.CACHE_FRESH, pt.CACHE_STALE) : |
|
58 |
raise exceptions.InternalServerError("Image cache not available: %s" % name) |
|
59 |
||
60 |
# load |
|
61 |
image.open() |
|
30 | 62 |
|
63 |
# cache |
|
89 | 64 |
IMAGE_CACHE[path] = image |
31
7eec7486a0af
dir index view, report if image not cached
Tero Marttila <terom@fixme.fi>
parents:
30
diff
changeset
|
65 |
|
89 | 66 |
return image |
67 |
||
68 |
||
69 |
||
70 |
### Handle request |
|
71 |
def handle_dir (req, name, path) : |
|
72 |
""" |
|
73 |
Handle request for a directory |
|
74 |
""" |
|
92
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
75 |
|
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
76 |
prefix = os.path.dirname(req.script_root).rstrip('/') |
89 | 77 |
|
92
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
78 |
return Response(render.dir_html(prefix, name, path), content_type="text/html") |
89 | 79 |
|
80 |
||
81 |
||
82 |
def handle_img_viewport (req, image, name) : |
|
83 |
""" |
|
84 |
Handle request for image viewport |
|
85 |
""" |
|
92
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
86 |
|
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
87 |
prefix = os.path.dirname(req.script_root).rstrip('/') |
89 | 88 |
|
89 |
# viewport |
|
92
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
90 |
return Response(render.img_html(prefix, name, image), content_type="text/html") |
89 | 91 |
|
92 |
||
93 |
def handle_img_region (req, image) : |
|
94 |
""" |
|
95 |
Handle request for an image region |
|
96 |
""" |
|
97 |
||
98 |
# specific image |
|
99 |
width = int(req.args['w']) |
|
100 |
height = int(req.args['h']) |
|
101 |
cx = int(req.args['cx']) |
|
102 |
cy = int(req.args['cy']) |
|
103 |
zoom = int(req.args.get('zl', "0")) |
|
92
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
104 |
|
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
105 |
try : |
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
106 |
# yay full render |
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
107 |
return Response(render.img_png_region(image, cx, cy, zoom, width, height), content_type="image/png") |
89 | 108 |
|
92
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
109 |
except ValueError, ex : |
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
110 |
# too large |
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
111 |
raise exceptions.Forbidden(str(ex)) |
89 | 112 |
|
113 |
||
114 |
def handle_img_tile (req, image) : |
|
115 |
""" |
|
116 |
Handle request for image tile |
|
117 |
""" |
|
118 |
||
119 |
# tile |
|
120 |
x = int(req.args['x']) |
|
121 |
y = int(req.args['y']) |
|
122 |
zoom = int(req.args.get('zl', "0")) |
|
123 |
||
124 |
# yay render |
|
92
e50ec4217fe6
separate non-wsgi render layer to render.png
Tero Marttila <terom@fixme.fi>
parents:
90
diff
changeset
|
125 |
return Response(render.img_png_tile(image, x, y, zoom), content_type="image/png") |
89 | 126 |
|
127 |
## Dispatch req to handle_img_* |
|
128 |
def handle_img (req, name, path) : |
|
129 |
""" |
|
130 |
Handle request for an image |
|
131 |
""" |
|
132 |
||
133 |
# get image object |
|
134 |
image = get_image(name, path) |
|
30 | 135 |
|
136 |
# what view? |
|
137 |
if not req.args : |
|
89 | 138 |
return handle_img_viewport(req, image, name) |
30 | 139 |
|
40 | 140 |
elif 'w' in req.args and 'h' in req.args and 'cx' in req.args and 'cy' in req.args : |
89 | 141 |
return handle_img_region(req, image) |
40 | 142 |
|
30 | 143 |
elif 'x' in req.args and 'y' in req.args : |
89 | 144 |
return handle_img_tile(req, image) |
145 |
||
30 | 146 |
else : |
147 |
raise exceptions.BadRequest("Unknown args") |
|
89 | 148 |
|
149 |
||
150 |
||
151 |
## Dispatch request to handle_* |
|
152 |
def handle_req (req) : |
|
153 |
""" |
|
154 |
Main request handler |
|
155 |
""" |
|
156 |
||
157 |
# decode req |
|
158 |
name, path = get_req_path(req) |
|
159 |
||
160 |
# determine dir/image |
|
161 |
if os.path.isdir(path) : |
|
162 |
# directory |
|
163 |
return handle_dir(req, name, path) |
|
164 |
||
165 |
elif not os.path.exists(path) : |
|
166 |
# no such file |
|
167 |
raise exceptions.NotFound(name) |
|
168 |
||
169 |
elif not name or not name.endswith('.png') : |
|
170 |
# invalid file |
|
171 |
raise exceptions.BadRequest("Not a PNG file") |
|
172 |
||
173 |
else : |
|
174 |
# image |
|
175 |
return handle_img(req, name, path) |
|
176 |
||
177 |
||
178 |
||
30 | 179 |
|
180 |
@responder |
|
181 |
def application (env, start_response) : |
|
89 | 182 |
""" |
183 |
Main WSGI entry point |
|
184 |
""" |
|
185 |
||
30 | 186 |
req = Request(env, start_response) |
187 |
||
188 |
try : |
|
89 | 189 |
return handle_req(req) |
30 | 190 |
|
191 |
except exceptions.HTTPException, e : |
|
192 |
return e |
|
193 |