/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/renderer.py
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/Thuban/UI/renderer.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 938 - (show annotations)
Tue May 20 15:25:10 2003 UTC (21 years, 9 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 18676 byte(s)
(MapRenderer.render_map): Add support for rendering RasterLayer layers.
(MapRenderer.draw_raster_layer): Actually method that calls
        the GDALWarp python wrapper and constructs an image from the
        data returned.

1 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Bernhard Herzog <[email protected]>
4 # Jonathan Coles <[email protected]>
5 # Frank Koormann <[email protected]>
6 #
7 # This program is free software under the GPL (>=v2)
8 # Read the file COPYING coming with Thuban for details.
9
10 __version__ = "$Revision$"
11
12 import cStringIO
13
14 from Thuban import _
15
16 from wxPython.wx import wxMemoryDC, wxEmptyBitmap, \
17 wxPoint, wxRect, wxPen, wxBrush, wxFont, \
18 wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
19 wxBLACK_PEN, wxRED_PEN, wxBLACK, \
20 wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
21 wxBitmap, wxImageFromBitmap, wxBitmapFromImage, \
22 wxImageFromStream, wxBITMAP_TYPE_BMP
23
24 from wxproj import draw_polygon_shape, draw_polygon_init
25 from gdalwarp import ProjectRasterFile
26
27 from Thuban.UI.common import Color2wxColour
28 from Thuban.UI.classifier import ClassDataPreviewer
29 from Thuban.UI.scalebar import ScaleBar
30
31 from Thuban.Model.layer import Layer, RasterLayer, \
32 SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
33 from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
34 ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE
35
36 from Thuban.Model.classification import Classification
37 from Thuban.Model.color import Color
38
39 class MapRenderer:
40
41 """Class to render a map onto a wxDC"""
42
43 honor_visibility = 1
44
45 def __init__(self, dc, scale, offset, resolution = 72.0,
46 honor_visibility = None):
47 """Inititalize the renderer.
48
49 dc -- the wxPython DC to render on
50 scale, offset -- the scale factor and translation to convert
51 between projected coordinates and the DC coordinates
52
53 resolution -- the assumed resolution of the DC. Used to convert
54 absolute lengths like font sizes to DC coordinates
55
56 honor_visibility -- boolean. If true, honor the visibility flag
57 of the layers, otherwise draw all layers. If None, use
58 the renderer's default.
59 """
60 # resolution in pixel/inch
61
62 self.dc = dc
63 self.scale = scale
64 self.offset = offset
65 if honor_visibility is not None:
66 self.honor_visibility = honor_visibility
67 # store the resolution in pixel/point because it's more useful
68 # later.
69 self.resolution = resolution / 72.0
70
71 def render_map(self, map):
72 self.map = map
73 seenRaster = True
74
75 #
76 # This is only a good optimization if there is only one
77 # raster layer and the image covers the entire window (as
78 # it currently does).
79 #
80 for layer in map.Layers():
81 if isinstance(layer, RasterLayer):
82 seenRaster = False
83 break
84
85 for layer in map.Layers():
86 # if honor_visibility is true, only draw visible layers,
87 # otherwise draw all layers
88 if not self.honor_visibility or layer.Visible():
89 if isinstance(layer, Layer) and seenRaster:
90 self.draw_shape_layer(layer)
91 elif isinstance(layer, RasterLayer):
92 self.draw_raster_layer(layer)
93 seenRaster = True
94
95 self.draw_label_layer(map.LabelLayer())
96
97 def draw_shape_layer(self, layer):
98 scale = self.scale
99 offx, offy = self.offset
100
101 map_proj = self.map.projection
102 layer_proj = layer.projection
103
104 shapetype = layer.ShapeType()
105
106 brush = wxTRANSPARENT_BRUSH
107 pen = wxTRANSPARENT_PEN
108
109 old_prop = None
110 old_group = None
111 lc = layer.GetClassification()
112 field = lc.GetField()
113 defaultGroup = lc.GetDefaultGroup()
114
115
116 if shapetype != SHAPETYPE_POINT:
117 polygon_render_param = self.polygon_render_param(layer)
118
119 if shapetype == SHAPETYPE_POINT:
120 draw_func = lambda i: \
121 self.draw_point_shape(layer, i)
122 else:
123 draw_func = lambda i: \
124 self.draw_polygon_shape(polygon_render_param, i, pen, brush)
125
126 for i in self.layer_ids(layer):
127
128 if field is None:
129 group = defaultGroup
130 else:
131 record = layer.table.ReadRowAsDict(i)
132 assert record is not None
133 group = lc.FindGroup(record[field])
134
135
136 if not group.IsVisible():
137 continue
138
139
140 # don't recreate new objects if they are the same as before
141 if group is not old_group:
142 old_group = group
143
144 prop = group.GetProperties()
145
146 if prop != old_prop:
147 old_prop = prop
148
149 if shapetype == SHAPETYPE_ARC:
150 fill = Color.Transparent
151 else:
152 fill = prop.GetFill()
153
154
155 if fill is Color.Transparent:
156 brush = wxTRANSPARENT_BRUSH
157 else:
158 color = Color2wxColour(fill)
159 brush = wxBrush(color, wxSOLID)
160
161 stroke = prop.GetLineColor()
162 stroke_width = prop.GetLineWidth()
163 if stroke is Color.Transparent:
164 pen = wxTRANSPARENT_PEN
165 else:
166 color = Color2wxColour(stroke)
167 pen = wxPen(color, stroke_width, wxSOLID)
168
169 if shapetype == SHAPETYPE_POINT:
170 self.dc.SetBrush(brush)
171 self.dc.SetPen(pen)
172
173 draw_func(i)
174
175 def draw_raster_layer(self, layer):
176 data = None
177 offx, offy = self.offset
178 width, height = self.dc.GetSizeTuple()
179
180 inProj = ""
181 proj = layer.GetProjection()
182 if proj is not None:
183 for p in proj.GetAllParameters():
184 inProj += "+" + p + " "
185
186 outProj = ""
187 proj = self.map.GetProjection()
188 if proj is not None:
189 for p in proj.GetAllParameters():
190 outProj += "+" + p + " "
191
192 print "self.scale: ", self.scale, offx, offy, width, height
193 xmin = (0 - offx) / self.scale
194 ymin = (offy - height) / self.scale
195 xmax = (width - offx) / self.scale
196 ymax = (offy - 0) / self.scale
197
198 try:
199 data = ProjectRasterFile(
200 layer.GetImageFilename(),
201 inProj,
202 outProj,
203 str(xmin), str(ymin), str(xmax), str(ymax),
204 "", str(width), str(height));
205 except (AttributeError, IOError, ValueError):
206 pass
207 else:
208 if data is not None:
209 stream = cStringIO.StringIO(data)
210 image = wxImageFromStream(stream, wxBITMAP_TYPE_BMP)
211 bitmap = wxBitmapFromImage(image)
212 self.dc.BeginDrawing()
213 self.dc.DrawBitmap(bitmap, 0, 0)
214 self.dc.EndDrawing()
215
216 def layer_ids(self, layer):
217 """Return the shape ids of the given layer that have to be drawn.
218
219 The default implementation simply returns all ids in the layer.
220 Override in derived classes to be more precise.
221 """
222 return range(layer.NumShapes())
223
224 def polygon_render_param(self, layer):
225 """Return the low-lever render parameter for the layer"""
226 offx, offy = self.offset
227 return draw_polygon_init(layer.shapefile, self.dc,
228 self.map.projection,
229 layer.projection,
230 self.scale, -self.scale,
231 offx, offy)
232
233 def draw_polygon_shape(self, draw_polygon_info, index, pen, brush):
234 draw_polygon_shape(draw_polygon_info, index, pen, brush)
235
236 def projected_points(self, layer, index):
237 proj = self.map.GetProjection()
238 if proj is not None:
239 forward = proj.Forward
240 else:
241 forward = None
242 proj = layer.GetProjection()
243 if proj is not None:
244 inverse = proj.Inverse
245 else:
246 inverse = None
247 shape = layer.Shape(index)
248 points = []
249 scale = self.scale
250 offx, offy = self.offset
251 for x, y in shape.Points():
252 if inverse:
253 x, y = inverse(x, y)
254 if forward:
255 x, y = forward(x, y)
256 points.append(wxPoint(x * scale + offx,
257 -y * scale + offy))
258 return points
259
260 def draw_arc_shape(self, layer, index):
261 points = self.projected_points(layer, index)
262 self.dc.DrawLines(points)
263
264 def draw_point_shape(self, layer, index):
265 pp = self.projected_points(layer, index)
266
267 if len(pp) == 0: return # ignore Null Shapes which have no points
268
269 p = pp[0]
270 radius = self.resolution * 5
271 self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)
272
273 def draw_label_layer(self, layer):
274 scale = self.scale
275 offx, offy = self.offset
276
277 font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
278 self.dc.SetFont(font)
279
280 map_proj = self.map.projection
281 if map_proj is not None:
282 forward = map_proj.Forward
283 else:
284 forward = None
285
286 for label in layer.Labels():
287 x = label.x
288 y = label.y
289 text = label.text
290 if forward:
291 x, y = forward(x, y)
292 x = x * scale + offx
293 y = -y * scale + offy
294 width, height = self.dc.GetTextExtent(text)
295 if label.halign == ALIGN_LEFT:
296 # nothing to be done
297 pass
298 elif label.halign == ALIGN_RIGHT:
299 x = x - width
300 elif label.halign == ALIGN_CENTER:
301 x = x - width/2
302 if label.valign == ALIGN_TOP:
303 # nothing to be done
304 pass
305 elif label.valign == ALIGN_BOTTOM:
306 y = y - height
307 elif label.valign == ALIGN_CENTER:
308 y = y - height/2
309 self.dc.DrawText(text, x, y)
310
311
312 class ScreenRenderer(MapRenderer):
313
314 # On the screen we want to see only visible layers by default
315 honor_visibility = 1
316
317 def RenderMap(self, map, region, selected_layer, selected_shapes):
318 """Render the map.
319
320 Only the given region (a tuple in window coordinates as returned
321 by a wxrect's asTuple method) needs to be redrawn. Highlight the
322 shapes given by the ids in selected_shapes in the
323 selected_layer.
324 """
325 self.update_region = region
326 self.selected_layer = selected_layer
327 self.selected_shapes = selected_shapes
328 self.render_map(map)
329
330 def draw_shape_layer(self, layer):
331 MapRenderer.draw_shape_layer(self, layer)
332 if layer is self.selected_layer and self.selected_shapes:
333 pen = wxPen(wxBLACK, 3, wxSOLID)
334 brush = wxBrush(wxBLACK, wxCROSS_HATCH)
335
336 shapetype = layer.ShapeType()
337 if shapetype == SHAPETYPE_POLYGON:
338 offx, offy = self.offset
339 renderparam = self.polygon_render_param(layer)
340 func = self.draw_polygon_shape
341 args = (pen, brush)
342 elif shapetype == SHAPETYPE_ARC:
343 renderparam = self.polygon_render_param(layer)
344 func = self.draw_polygon_shape
345 args = (pen, None)
346 elif shapetype == SHAPETYPE_POINT:
347 renderparam = layer
348 self.dc.SetBrush(brush)
349 self.dc.SetPen(pen)
350 func = self.draw_point_shape
351 args = ()
352 else:
353 raise TypeError(_("Unhandled shape type %s") % shapetype)
354
355 for index in self.selected_shapes:
356 func(renderparam, index, *args)
357
358
359 def layer_ids(self, layer):
360 """Return the shapeids covered by the region that has to be redrawn
361
362 Call the layer's ShapesInRegion method to determine the ids so
363 that it can use the quadtree.
364 """
365 # FIXME: the quad-tree should be built from the projected
366 # coordinates not the lat-long ones because it's not trivial to
367 # determine an appropriate rectangle in lat-long for a given
368 # rectangle in projected coordinates which we have to start from
369 # here.
370 proj = self.map.projection
371 if proj is not None:
372 inverse = proj.Inverse
373 else:
374 inverse = None
375
376 scale = self.scale
377 offx, offy = self.offset
378 xs = []
379 ys = []
380 x, y, width, height = self.update_region
381 for winx, winy in ((x, y), (x + width, y),
382 (x + width, y + height), (x, y + height)):
383 px = (winx - offx) / scale
384 py = -(winy - offy) / scale
385 if inverse:
386 px, py = inverse(px, py)
387 xs.append(px)
388 ys.append(py)
389 left = min(xs)
390 right = max(xs)
391 top = max(ys)
392 bottom = min(ys)
393
394 return layer.ShapesInRegion((left, bottom, right, top))
395
396
397 class ExportRenderer(ScreenRenderer):
398
399 honor_visibility = 1
400
401 def RenderMap(self, map, region, mapregion,
402 selected_layer, selected_shapes ):
403 """Render the map.
404
405 The rendering device has been specified during initialisation.
406 The device border distance was set in Thuban.UI.view.OutputTranform().
407
408 RenderMap renders a frame set (one page frame, one around
409 legend/scalebar and one around the map), the map, the legend and the
410 scalebar on the given DC. The map is rendered with the region displayed
411 in the canvas view, centered on the area available for map display.
412 """
413
414 self.update_region = region
415 self.selected_layer = selected_layer
416 self.selected_shapes = selected_shapes
417
418 # Get some dimensions
419 llx, lly, urx, ury = region
420 self.mapregion = mapregion
421 mminx, mminy, mmaxx, mmaxy = self.mapregion
422
423 # Manipulate the offset to position the map
424 offx, offy = self.offset
425 # 1. Shift to corner of map drawing area
426 offx = offx + mminx
427 offy = offy + mminy
428
429 # 2. Center the map on the map drawing area:
430 # region identifies the region on the canvas view:
431 # center of map drawing area - half the size of region: rendering origin
432 self.shiftx = (mmaxx - mminx)*0.5 - (urx - llx)*0.5
433 self.shifty = (mmaxy - mminy)*0.5 - (ury - lly)*0.5
434
435 self.offset = (offx+self.shiftx, offy+self.shifty)
436
437 # Draw the map
438 self.dc.BeginDrawing()
439 self.dc.DestroyClippingRegion()
440 self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
441 urx, ury)
442 self.render_map(map)
443 self.dc.EndDrawing()
444
445 # Draw the rest (frames, legend, scalebar)
446 self.dc.BeginDrawing()
447 self.dc.DestroyClippingRegion()
448
449 # Force the font for Legend drawing
450 font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
451 self.dc.SetFont(font)
452
453 self.render_frame(region)
454 self.render_legend(map)
455 self.render_scalebar(map)
456 self.dc.EndDrawing()
457
458 def render_frame(self, region):
459 """Render the frames for map and legend/scalebar."""
460
461 dc = self.dc
462 dc.SetPen(wxBLACK_PEN)
463 dc.SetBrush(wxTRANSPARENT_BRUSH)
464
465 # Dimension stuff
466 width, height = dc.GetSizeTuple()
467 mminx, mminy, mmaxx, mmaxy = self.mapregion
468
469 # Page Frame
470 dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
471
472 # Map Frame
473 llx, lly, urx, ury = region
474 dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
475
476 # Legend Frame
477 dc.DrawRectangle(mmaxx+10,mminy,(width-20) - (mmaxx+10), mmaxy-mminy)
478
479 dc.DestroyClippingRegion()
480 dc.SetClippingRegion(mmaxx+10,mminy,
481 (width-20) - (mmaxx+10), mmaxy-mminy)
482
483 def render_legend(self, map):
484 """Render the legend on the Map."""
485
486 previewer = ClassDataPreviewer()
487 dc = self.dc
488 dc.SetPen(wxBLACK_PEN)
489 dc.SetBrush(wxTRANSPARENT_BRUSH)
490
491 # Dimension stuff
492 width, height = dc.GetSizeTuple()
493 mminx, mminy, mmaxx, mmaxy = self.mapregion
494 textwidth, textheight = dc.GetTextExtent("0")
495 iconwidth = textheight
496 iconheight = textheight
497 stepy = textheight+3
498 dx = 10
499 posx = mmaxx + 10 + 5 # 10 pix distance mapframe/legend frame,
500 # 5 pix inside legend frame
501 posy = mminy + 5 # 5 pix inside legend frame
502
503 # Render the legend
504 dc.SetTextForeground(wxBLACK)
505 if map.HasLayers():
506 for l in map.Layers():
507 if l.Visible():
508 # Render title
509 dc.DrawText(l.Title(), posx, posy)
510 posy+=stepy
511 # Render classification
512 clazz = l.GetClassification()
513 shapeType = l.ShapeType()
514 for g in clazz:
515 if g.IsVisible():
516 previewer.Draw(dc,
517 wxRect(posx+dx, posy, iconwidth, iconheight),
518 g.GetProperties(), shapeType)
519 dc.DrawText(g.GetDisplayText(),
520 posx+2*dx+iconwidth, posy)
521 posy+=stepy
522
523 def render_scalebar(self, map):
524 """Render the scalebar."""
525
526 scalebar = ScaleBar(map)
527
528 # Dimension stuff
529 width, height = self.dc.GetSizeTuple()
530 mminx, mminy, mmaxx, mmaxy = self.mapregion
531
532 # Render the scalebar
533 scalebar.DrawScaleBar(self.scale, self.dc,
534 (mmaxx+10+5, mmaxy-25),
535 ((width-15-5) - (mmaxx+10+5),20)
536 )
537 # 10 pix between map and legend frame, 5 pix inside legend frame
538 # 25 pix from the legend frame bottom line
539 # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
540 # Height: 20
541
542 class PrinterRenderer(ExportRenderer):
543
544 # Printing as well as Export / Screen display only the visible layer.
545 honor_visibility = 1
546

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26