/[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 1552 - (show annotations)
Wed Aug 6 17:21:32 2003 UTC (21 years, 7 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 10818 byte(s)
* Thuban/UI/renderer.py (MapRenderer): Most of the code/methods in
this class is now in BaseRenderer. This class is now practically
only a specialization of BaseRenderer for rendering to an actual
wx DC.
(ScreenRenderer.draw_shape_layer): Use self.low_level_renderer()
to get the shapetype specific rendering functions.

* test/test_baserenderer.py: New. Test cases for BaseRenderer

* Thuban/UI/view.py (MapCanvas.__init__): New instance variable
error_on_redraw to guard agains endless loops and stack overflows
when there's a bug in the rendering code that raises exceptions.
(MapCanvas.OnIdle, MapCanvas._do_redraw): Split the actual
rendering into a separate method _do_redraw so that error handling
is a bit easier. When an exception occurs, set error_on_redraw to
true. When it's true on entry to OnIdle do nothing and return
immediately.

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 wxPoint, wxRect, wxPen, wxBrush, wxFont, \
17 wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
18 wxBLACK_PEN, wxBLACK, wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
19 wxBitmapFromImage, wxImageFromStream, wxBITMAP_TYPE_BMP
20
21 from wxproj import draw_polygon_shape, draw_polygon_init
22
23 from Thuban.UI.common import Color2wxColour
24 from Thuban.UI.classifier import ClassDataPreviewer
25 from Thuban.UI.scalebar import ScaleBar
26
27 from Thuban.Model.data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
28
29 from Thuban.Model.color import Transparent
30 import Thuban.Model.resource
31
32 from baserenderer import BaseRenderer
33
34 class MapRenderer(BaseRenderer):
35
36 """Class to render a map onto a wxDC"""
37
38 TRANSPARENT_PEN = wxTRANSPARENT_PEN
39 TRANSPARENT_BRUSH = wxTRANSPARENT_BRUSH
40
41 make_point = wxPoint
42
43 def tools_for_property(self, prop):
44 fill = prop.GetFill()
45 if fill is Transparent:
46 brush = self.TRANSPARENT_BRUSH
47 else:
48 brush = wxBrush(Color2wxColour(fill), wxSOLID)
49
50 stroke = prop.GetLineColor()
51 if stroke is Transparent:
52 pen = self.TRANSPARENT_PEN
53 else:
54 pen = wxPen(Color2wxColour(stroke), prop.GetLineWidth(), wxSOLID)
55 return pen, brush
56
57 def low_level_renderer(self, layer):
58 """Override inherited method to provide more efficient renderers
59
60 For point shapes, return self.draw_point_shape and layer just as
61 the base class method. For arc and polygon use the more
62 efficient wxproj.draw_polygon_shape and its corresponding
63 parameter created with wxproj.draw_polygon_init.
64 """
65 shapetype = layer.ShapeType()
66 if shapetype == SHAPETYPE_POINT:
67 func = self.draw_point_shape
68 param = layer
69 else:
70 offx, offy = self.offset
71 param = draw_polygon_init(layer.ShapeStore().Shapefile(), self.dc,
72 self.map.projection,
73 layer.projection,
74 self.scale, -self.scale,
75 offx, offy)
76 func = draw_polygon_shape
77 return func, param
78
79 def label_font(self):
80 return wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
81
82 def draw_raster_data(self, data):
83 stream = cStringIO.StringIO(data)
84 image = wxImageFromStream(stream, wxBITMAP_TYPE_BMP)
85 bitmap = wxBitmapFromImage(image)
86 self.dc.DrawBitmap(bitmap, 0, 0)
87
88
89 class ScreenRenderer(MapRenderer):
90
91 # On the screen we want to see only visible layers by default
92 honor_visibility = 1
93
94 def RenderMap(self, map, region, selected_layer, selected_shapes):
95 """Render the map.
96
97 Only the given region (a tuple in window coordinates as returned
98 by a wxrect's asTuple method) needs to be redrawn. Highlight the
99 shapes given by the ids in selected_shapes in the
100 selected_layer.
101 """
102 self.update_region = region
103 self.selected_layer = selected_layer
104 self.selected_shapes = selected_shapes
105 self.render_map(map)
106
107 def draw_shape_layer(self, layer):
108 MapRenderer.draw_shape_layer(self, layer)
109 if layer is self.selected_layer and self.selected_shapes:
110 pen = wxPen(wxBLACK, 3, wxSOLID)
111 brush = wxBrush(wxBLACK, wxCROSS_HATCH)
112
113 shapetype = layer.ShapeType()
114 func, param = self.low_level_renderer(layer)
115 args = (pen, brush)
116 for index in self.selected_shapes:
117 func(param, index, *args)
118
119 def layer_ids(self, layer):
120 """Return the shapeids covered by the region that has to be redrawn
121
122 Call the layer's ShapesInRegion method to determine the ids so
123 that it can use the quadtree.
124 """
125 # FIXME: the quad-tree should be built from the projected
126 # coordinates not the lat-long ones because it's not trivial to
127 # determine an appropriate rectangle in lat-long for a given
128 # rectangle in projected coordinates which we have to start from
129 # here.
130 proj = self.map.projection
131 if proj is not None:
132 inverse = proj.Inverse
133 else:
134 inverse = None
135
136 scale = self.scale
137 offx, offy = self.offset
138 xs = []
139 ys = []
140 x, y, width, height = self.update_region
141 for winx, winy in ((x, y), (x + width, y),
142 (x + width, y + height), (x, y + height)):
143 px = (winx - offx) / scale
144 py = -(winy - offy) / scale
145 if inverse:
146 px, py = inverse(px, py)
147 xs.append(px)
148 ys.append(py)
149 left = min(xs)
150 right = max(xs)
151 top = max(ys)
152 bottom = min(ys)
153
154 return layer.ShapesInRegion((left, bottom, right, top))
155
156
157 class ExportRenderer(ScreenRenderer):
158
159 honor_visibility = 1
160
161 def RenderMap(self, map, region, mapregion,
162 selected_layer, selected_shapes ):
163 """Render the map.
164
165 The rendering device has been specified during initialisation.
166 The device border distance was set in Thuban.UI.view.OutputTranform().
167
168 RenderMap renders a frame set (one page frame, one around
169 legend/scalebar and one around the map), the map, the legend and the
170 scalebar on the given DC. The map is rendered with the region displayed
171 in the canvas view, centered on the area available for map display.
172 """
173
174 self.update_region = region
175 self.selected_layer = selected_layer
176 self.selected_shapes = selected_shapes
177
178 # Get some dimensions
179 llx, lly, urx, ury = region
180 self.mapregion = mapregion
181 mminx, mminy, mmaxx, mmaxy = self.mapregion
182
183 # Manipulate the offset to position the map
184 offx, offy = self.offset
185 # 1. Shift to corner of map drawing area
186 offx = offx + mminx
187 offy = offy + mminy
188
189 # 2. Center the map on the map drawing area:
190 # region identifies the region on the canvas view:
191 # center of map drawing area - half the size of region: rendering origin
192 self.shiftx = (mmaxx - mminx)*0.5 - (urx - llx)*0.5
193 self.shifty = (mmaxy - mminy)*0.5 - (ury - lly)*0.5
194
195 self.offset = (offx+self.shiftx, offy+self.shifty)
196
197 # Draw the map
198 self.dc.BeginDrawing()
199 self.dc.DestroyClippingRegion()
200 self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
201 urx, ury)
202 self.render_map(map)
203 self.dc.EndDrawing()
204
205 # Draw the rest (frames, legend, scalebar)
206 self.dc.BeginDrawing()
207 self.dc.DestroyClippingRegion()
208
209 # Force the font for Legend drawing
210 font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
211 self.dc.SetFont(font)
212
213 self.render_frame(region)
214 self.render_legend(map)
215 self.render_scalebar(map)
216 self.dc.EndDrawing()
217
218 def render_frame(self, region):
219 """Render the frames for map and legend/scalebar."""
220
221 dc = self.dc
222 dc.SetPen(wxBLACK_PEN)
223 dc.SetBrush(wxTRANSPARENT_BRUSH)
224
225 # Dimension stuff
226 width, height = dc.GetSizeTuple()
227 mminx, mminy, mmaxx, mmaxy = self.mapregion
228
229 # Page Frame
230 dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
231
232 # Map Frame
233 llx, lly, urx, ury = region
234 dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
235
236 # Legend Frame
237 dc.DrawRectangle(mmaxx+10,mminy,(width-20) - (mmaxx+10), mmaxy-mminy)
238
239 dc.DestroyClippingRegion()
240 dc.SetClippingRegion(mmaxx+10,mminy,
241 (width-20) - (mmaxx+10), mmaxy-mminy)
242
243 def render_legend(self, map):
244 """Render the legend on the Map."""
245
246 previewer = ClassDataPreviewer()
247 dc = self.dc
248 dc.SetPen(wxBLACK_PEN)
249 dc.SetBrush(wxTRANSPARENT_BRUSH)
250
251 # Dimension stuff
252 width, height = dc.GetSizeTuple()
253 mminx, mminy, mmaxx, mmaxy = self.mapregion
254 textwidth, textheight = dc.GetTextExtent("0")
255 iconwidth = textheight
256 iconheight = textheight
257 stepy = textheight+3
258 dx = 10
259 posx = mmaxx + 10 + 5 # 10 pix distance mapframe/legend frame,
260 # 5 pix inside legend frame
261 posy = mminy + 5 # 5 pix inside legend frame
262
263 # Render the legend
264 dc.SetTextForeground(wxBLACK)
265 if map.HasLayers():
266 layers = map.Layers()
267 layers.reverse()
268 for l in layers:
269 if l.Visible():
270 # Render title
271 dc.DrawText(l.Title(), posx, posy)
272 posy+=stepy
273 if l.HasClassification():
274 # Render classification
275 clazz = l.GetClassification()
276 shapeType = l.ShapeType()
277 for g in clazz:
278 if g.IsVisible():
279 previewer.Draw(dc,
280 wxRect(posx+dx, posy,
281 iconwidth, iconheight),
282 g.GetProperties(), shapeType)
283 dc.DrawText(g.GetDisplayText(),
284 posx+2*dx+iconwidth, posy)
285 posy+=stepy
286
287 def render_scalebar(self, map):
288 """Render the scalebar."""
289
290 scalebar = ScaleBar(map)
291
292 # Dimension stuff
293 width, height = self.dc.GetSizeTuple()
294 mminx, mminy, mmaxx, mmaxy = self.mapregion
295
296 # Render the scalebar
297 scalebar.DrawScaleBar(self.scale, self.dc,
298 (mmaxx+10+5, mmaxy-25),
299 ((width-15-5) - (mmaxx+10+5),20)
300 )
301 # 10 pix between map and legend frame, 5 pix inside legend frame
302 # 25 pix from the legend frame bottom line
303 # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
304 # Height: 20
305
306 class PrinterRenderer(ExportRenderer):
307
308 # Printing as well as Export / Screen display only the visible layer.
309 honor_visibility = 1
310

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26