1 import mapnik
2 import settings
3 from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest, HttpResponseServerError, HttpResponseForbidden, Http404
4 from django.template import RequestContext
5 from django.shortcuts import get_object_or_404, render_to_response
6 from django.db import connection
7 from madrona.common import default_mimetypes as mimetypes
8 from madrona.common import utils
9 from madrona.staticmap.models import MapConfig
10 from madrona.features import get_feature_models, get_collection_models, get_feature_by_uid, get_model_by_uid
11 from madrona.features.models import FeatureCollection, SpatialFeature, PointFeature, PolygonFeature, LineFeature
12 from djmapnik.adapter import PostgisLayer
13 from madrona.common.utils import get_logger
14 from django.template.defaultfilters import slugify
15 log = get_logger()
16
17 try:
18 settings_dbname = settings.DATABASES['default']['NAME']
19 except:
20 settings_dbname = settings.DATABASE_NAME
21
23 default_style = mapnik.Style()
24 ps = mapnik.PolygonSymbolizer(mapnik.Color('#ffffff'))
25 ps.fill_opacity = 0.5
26 ls = mapnik.LineSymbolizer(mapnik.Color('#555555'),0.75)
27 ls.stroke_opacity = 0.5
28 r = mapnik.Rule()
29 r.symbols.append(ps)
30 r.symbols.append(ls)
31 r.symbols.append(mapnik.PointSymbolizer())
32 default_style.rules.append(r)
33 return default_style
34
36 """
37 Returns list of tuples representing mapnik layers
38 Tuple => (model_class, [pks])
39 Note: currently just a single pk per 'layer' which is
40 incredibly inefficient but the only way to ensure
41 proper layer ordering (??).
42 features = [ (Mpa, [49, 50]),
43 (Pipeline, [32, 31]),
44 (Shipwreck, [32, 31])
45 ]
46 """
47 feature_models = get_feature_models()
48 collection_models = get_collection_models()
49 features = []
50 for uid in uids:
51 log.debug("processing uid %s" % uid)
52 applabel, modelname, pk = uid.split('_')
53 model = get_model_by_uid("%s_%s" % (applabel,modelname))
54 feature = get_feature_by_uid(uid)
55
56 if user:
57 viewable, response = feature.is_viewable(user)
58 if not viewable:
59 continue
60
61 if model in collection_models:
62 collection = get_feature_by_uid(uid)
63 if user:
64 viewable, response = collection.is_viewable(user)
65 if not viewable:
66 continue
67 all_children = collection.feature_set(recurse=True)
68 children = [x for x in all_children if x.__class__ in feature_models]
69 for child in children:
70 features.append((child.__class__,[child.pk]))
71 else:
72 features.append((model,[int(pk)]))
73
74 return features
75
77 """
78 Given a set of staticmap features,
79 returns the bounding box required to zoom into those features.
80 Includes a configurable edge buffer
81 """
82 minx = 99999999.0
83 miny = 99999999.0
84 maxx = -99999999.0
85 maxy = -99999999.0
86 for model, pks in features:
87 try:
88 geomfield = model.mapnik_geomfield()
89 except AttributeError:
90 geomfield = 'geometry_final'
91
92 try:
93 ugeom = model.objects.filter(pk__in=pks).collect(field_name=geomfield).transform(srid,clone=True)
94 bbox = ugeom.extent
95 if bbox[0] < minx:
96 minx = bbox[0]
97 if bbox[1] < miny:
98 miny = bbox[1]
99 if bbox[2] > maxx:
100 maxx = bbox[2]
101 if bbox[3] > maxy:
102 maxy = bbox[3]
103 except TypeError as e:
104 log.error("Failed to get extent for %r with pks %r; Exception: \n%s" % (model, pks, e))
105 pass
106
107 width = maxx - minx
108 height = maxy - miny
109 buffer = .15
110
111
112 if width == 0:
113 if bbox[2] <= 180.1:
114 width = 0.1
115 else:
116 width = 1000
117 if height == 0:
118 if bbox[3] <= 90.1:
119 height = 0.1
120 else:
121 height = 1000
122
123
124
125 try:
126 if settings.STATICMAP_WIDTH_BUFFER is not None and settings.STATICMAP_HEIGHT_BUFFER is not None:
127 width_buffer = settings.STATICMAP_WIDTH_BUFFER
128 height_buffer = settings.STATICMAP_HEIGHT_BUFFER
129 else:
130 raise AttributeError
131 except AttributeError:
132 width_buffer = width * buffer
133 height_buffer = height * buffer
134
135 return minx - width_buffer, miny - height_buffer, maxx + width_buffer, maxy + height_buffer
136
137
139 """
140 Generic link version of the staticmap view
141 """
142 width, height = None, None
143 uids = [i.uid for i in instances]
144 filename = '_'.join([slugify(i.name) for i in instances])
145 autozoom = settings.STATICMAP_AUTOZOOM
146 bbox = None
147 show_extent = False
148
149 img = draw_map(uids, request.user, width, height, autozoom, bbox, show_extent, map_name)
150
151 response = HttpResponse()
152 response['Content-length'] = len(img)
153 response['Content-Type'] = 'image/png'
154 response['Content-Disposition'] = 'attachment; filename=%s.png' % filename
155 response.write(img)
156 return response
157
158
159 -def show(request, map_name="default"):
160
161 try:
162 width = int(request.REQUEST['width'])
163 height = int(request.REQUEST['height'])
164 except:
165
166 width, height = None, None
167
168 if 'uids' in request.REQUEST:
169 uids = str(request.REQUEST['uids']).split(',')
170 else:
171 uids = []
172
173 if "autozoom" in request.REQUEST and request.REQUEST['autozoom'].lower() == 'true':
174 autozoom = True
175 else:
176 autozoom = False
177
178 if "bbox" in request.REQUEST:
179 bbox = request.REQUEST['bbox']
180 else:
181 bbox = None
182
183 if "show_extent" in request.REQUEST and request.REQUEST['show_extent'].lower() == 'true':
184 show_extent = True
185 else:
186 show_extent = False
187
188 img = draw_map(uids, request.user, width, height, autozoom, bbox, show_extent, map_name)
189
190 if 'attachment' in request.REQUEST and request.REQUEST['attachment'].lower() == 'true':
191 attach = True
192 else:
193 attach = False
194
195 response = HttpResponse()
196 response['Content-length'] = len(img)
197 response['Content-Type'] = 'image/png'
198 if attach:
199 response['Content-Disposition'] = 'attachment; filename=madrona.png'
200 response.write(img)
201
202 return response
203
204 -def draw_map(uids, user, width, height, autozoom=False, bbox=None, show_extent=False, map_name='default'):
205 """Display a map with the study region geometry. """
206
207 conn = connection.settings_dict
208 testing = False
209 if conn['NAME'] != settings_dbname:
210 testing = True
211
212 map = get_object_or_404(MapConfig,mapname=map_name)
213 if not width:
214 width = map.default_width
215 if not height:
216 height = map.default_height
217 mapfile = str(map.mapfile.path)
218
219
220 draw = mapnik.Image(width,height)
221 m = mapnik.Map(width,height)
222
223
224
225
226 xmltext = open(mapfile).read()
227
228 xmltext = xmltext.replace("[[MEDIA_ROOT]]",settings.MEDIA_ROOT)
229 mapnik.load_map_from_string(m, xmltext)
230 log.debug("Completed load_map_from_string(), Map object is %r" % m)
231
232
233 features = get_features(uids,user)
234 for model, pks in features:
235 try:
236 geomfield = model.mapnik_geomfield()
237 except AttributeError:
238 geomfield = 'geometry_final'
239
240 if geomfield not in [str(x.name) for x in model._meta.fields]:
241 continue
242
243 if not issubclass(model, FeatureCollection):
244 try:
245 style = model.mapnik_style()
246 except AttributeError:
247 style = default_style()
248 style_name = str('%s_style' % model.model_uid())
249 m.append_style(style_name, style)
250 if testing:
251 adapter = PostgisLayer(model.objects.filter(pk__in=pks), field_name=geomfield, persist_connection=False)
252 else:
253 adapter = PostgisLayer(model.objects.filter(pk__in=pks), field_name=geomfield)
254 lyr = adapter.to_mapnik()
255 lyr.styles.append(style_name)
256 m.layers.append(lyr)
257
258
259
260 x1, y1 = map.default_x1, map.default_y1
261 x2, y2 = map.default_x2, map.default_y2
262
263 if not bbox and autozoom and features and len(features) > 0:
264 x1, y1, x2, y2 = auto_extent(features, map.default_srid)
265 if bbox:
266 try:
267 x1, y1, x2, y2 = [float(x) for x in bbox.split(',')]
268 except:
269 pass
270
271 bbox = mapnik.Box2d(mapnik.Coord(x1,y1), mapnik.Coord(x2,y2))
272
273 if show_extent and features and len(features) > 0:
274
275
276 x1, y1, x2, y2 = auto_extent(features, map.default_srid)
277
278 ps = mapnik.PolygonSymbolizer(mapnik.Color('#ffffff'))
279 ps.fill_opacity = 0.8
280 ls = mapnik.LineSymbolizer(mapnik.Color('#ff0000'),2.0)
281 r = mapnik.Rule()
282 r.symbols.append(ps)
283 r.symbols.append(ls)
284 extent_style = mapnik.Style()
285 extent_style.rules.append(r)
286 m.append_style('extent_style', extent_style)
287 lyr = mapnik.Layer("Features Extent")
288 bbox_sql = """
289 (select 1 as id, st_setsrid(st_makebox2d(st_point(%s,%s),st_point(%s,%s)), %s) as geometry_final) as aoi
290 """ % (x1,y1,x2,y2,map.default_srid)
291 lyr.datasource = mapnik.PostGIS(host=connection.settings_dict['HOST'],
292 user=connection.settings_dict['USER'],
293 password=connection.settings_dict['PASSWORD'],
294 dbname=connection.settings_dict['NAME'],
295 table=bbox_sql,
296 geometry_field='geometry_final',
297 estimate_extent=False,
298 persist_connection=not testing,
299 extent='%s,%s,%s,%s' % (x1,y1,x2,y2))
300 lyr.styles.append('extent_style')
301 m.layers.append(lyr)
302
303
304 m.zoom_to_box(bbox)
305
306 mapnik.render(m, draw)
307 img = draw.tostring('png')
308
309 if testing:
310 del m
311
312 return img
313