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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 909 - (hide 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 bh 424 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 bh 6 # Authors:
3     # Bernhard Herzog <[email protected]>
4 jonathan 416 # Jonathan Coles <[email protected]>
5 frank 909 # Frank Koormann <[email protected]>
6 bh 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 jonathan 882 from Thuban import _
13    
14 frank 909 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 bh 6
19 jonathan 676 from wxproj import draw_polygon_shape, draw_polygon_init
20 bh 6
21 jonathan 882 from Thuban.UI.common import Color2wxColour
22 frank 909 from Thuban.UI.classifier import ClassDataPreviewer
23     from Thuban.UI.scalebar import ScaleBar
24 jan 374
25 bh 6 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 jonathan 394 from Thuban.Model.classification import Classification
31 jonathan 416 from Thuban.Model.color import Color
32 bh 6
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 jonathan 574
56 bh 6 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 jonathan 362 brush = wxTRANSPARENT_BRUSH
84     pen = wxTRANSPARENT_PEN
85    
86     old_prop = None
87 jonathan 798 old_group = None
88 jonathan 468 lc = layer.GetClassification()
89     field = lc.GetField()
90 jonathan 798 defaultGroup = lc.GetDefaultGroup()
91 jonathan 468
92 jonathan 676
93     if shapetype != SHAPETYPE_POINT:
94 bh 686 polygon_render_param = self.polygon_render_param(layer)
95 jonathan 676
96 jonathan 798 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 jonathan 362 for i in self.layer_ids(layer):
104    
105 jonathan 798 if field is None:
106     group = defaultGroup
107 jonathan 468 else:
108 bh 837 record = layer.table.ReadRowAsDict(i)
109 jonathan 798 assert record is not None
110     group = lc.FindGroup(record[field])
111 jonathan 362
112 jonathan 642
113     if not group.IsVisible():
114     continue
115    
116 jonathan 676
117 jonathan 442 # don't recreate new objects if they are the same as before
118 jonathan 798 if group is not old_group:
119     old_group = group
120 jonathan 362
121 jonathan 798 prop = group.GetProperties()
122 jonathan 676
123 jonathan 798 if prop != old_prop:
124     old_prop = prop
125 bh 686
126 jonathan 798 if shapetype == SHAPETYPE_ARC:
127     fill = Color.Transparent
128     else:
129     fill = prop.GetFill()
130 bh 686
131 jonathan 676
132 jonathan 798 if fill is Color.Transparent:
133     brush = wxTRANSPARENT_BRUSH
134     else:
135     color = Color2wxColour(fill)
136     brush = wxBrush(color, wxSOLID)
137 bh 686
138 jonathan 798 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 bh 6
146 jonathan 798 if shapetype == SHAPETYPE_POINT:
147     self.dc.SetBrush(brush)
148     self.dc.SetPen(pen)
149    
150     draw_func(i)
151    
152 bh 144 def layer_ids(self, layer):
153     """Return the shape ids of the given layer that have to be drawn.
154 bh 686
155 bh 144 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 bh 686 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 bh 6
169 bh 686 def draw_polygon_shape(self, draw_polygon_info, index, pen, brush):
170     draw_polygon_shape(draw_polygon_info, index, pen, brush)
171    
172 bh 6 def projected_points(self, layer, index):
173 jonathan 798 proj = self.map.GetProjection()
174 bh 6 if proj is not None:
175     forward = proj.Forward
176     else:
177     forward = None
178 jonathan 798 proj = layer.GetProjection()
179 bh 6 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 jonathan 588 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 bh 6 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 bh 535 def RenderMap(self, map, region, selected_layer, selected_shapes):
254 bh 144 """Render the map.
255    
256 bh 148 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 bh 535 shapes given by the ids in selected_shapes in the
259     selected_layer.
260 bh 144 """
261     self.update_region = region
262 bh 6 self.selected_layer = selected_layer
263 bh 535 self.selected_shapes = selected_shapes
264 bh 6 self.render_map(map)
265    
266     def draw_shape_layer(self, layer):
267     MapRenderer.draw_shape_layer(self, layer)
268 bh 535 if layer is self.selected_layer and self.selected_shapes:
269 bh 6 pen = wxPen(wxBLACK, 3, wxSOLID)
270     brush = wxBrush(wxBLACK, wxCROSS_HATCH)
271 bh 535
272 bh 6 shapetype = layer.ShapeType()
273     if shapetype == SHAPETYPE_POLYGON:
274 bh 686 offx, offy = self.offset
275     renderparam = self.polygon_render_param(layer)
276 bh 535 func = self.draw_polygon_shape
277     args = (pen, brush)
278 bh 290 elif shapetype == SHAPETYPE_ARC:
279 bh 686 renderparam = self.polygon_render_param(layer)
280 bh 535 func = self.draw_polygon_shape
281     args = (pen, None)
282     elif shapetype == SHAPETYPE_POINT:
283 bh 686 renderparam = layer
284 bh 6 self.dc.SetBrush(brush)
285     self.dc.SetPen(pen)
286 bh 535 func = self.draw_point_shape
287     args = ()
288     else:
289     raise TypeError(_("Unhandled shape type %s") % shapetype)
290 bh 6
291 bh 535 for index in self.selected_shapes:
292 bh 686 func(renderparam, index, *args)
293 bh 535
294 bh 686
295 bh 144 def layer_ids(self, layer):
296     """Return the shapeids covered by the region that has to be redrawn
297 bh 6
298 bh 144 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 bh 148 x, y, width, height = self.update_region
317 bh 144 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 frank 909 class ExportRenderer(ScreenRenderer):
334 bh 6
335 frank 909 honor_visibility = 1
336    
337     def RenderMap(self, map, region, mapregion,
338     selected_layer, selected_shapes ):
339     """Render the map.
340 bh 6
341 frank 909 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