/[thuban]/branches/greater-ms3/thuban/Thuban/UI/renderer.py
ViewVC logotype

Contents of /branches/greater-ms3/thuban/Thuban/UI/renderer.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1758 - (show annotations)
Fri Sep 26 14:43:56 2003 UTC (21 years, 5 months ago) by frank
File MIME type: text/x-python
File size: 18832 byte(s)
Thuban/UI/renderer.py (ExportRenderer.RenderMap),
Thuban/UI/view.py (MapPrintout.draw_on_dc, MapCanvase.Export):
	Added mainwindow title to export/printout as caption.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26