/[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 909 - (show annotations)
Fri May 16 16:23:12 2003 UTC (21 years, 9 months ago) by frank
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 16554 byte(s)
ExportRenderer): New, derived from ScreenRenderer. Renders
	Map, Legend and Scalebar for export.
(PrinterRenderer): New, derived from ExportRenderer. Replaces the old
	PrintRender.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26