/[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 1866 - (show annotations)
Mon Oct 27 13:01:58 2003 UTC (21 years, 4 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 11866 byte(s)
Several rendering changes:

 - Render the selection into a separate bitmap so that only that
   bitmap needs to be redrawn when the selection changes

 - Render incrementally showing previews and allowing interaction
   before rendering is complete

 - Update the renderer interface a bit. Most parameters of
   RenderMap are now parameters of the constructor

* Thuban/UI/baserenderer.py (BaseRenderer.__init__): Add the map
and the update region as parameters. Update the doc-string
(BaseRenderer.render_map_incrementally): New. Generator function
to renders the map incrementally
(BaseRenderer.render_map): Remove the map argument (it's now in
the constructor) and simply iterate over the
render_map_incrementally generator to draw the map.
(BaseRenderer.draw_shape_layer_incrementally)
(BaseRenderer.draw_shape_layer): Renamed to
draw_shape_layer_incrementally and changed into a generator that
yields True every 500 shapes. Used by render_map_incrementally to
render shape layers incrementally

* Thuban/UI/renderer.py (ScreenRenderer.RenderMap): Removed the
map and region parameters which are now in the constructor
(ScreenRenderer.RenderMapIncrementally): New. Public frontend for
the inherited render_map_incrementally.
(BaseRenderer.draw_shape_layer): Removed.
(ScreenRenderer.draw_selection_incrementally): New. The selection
drawing part of the removed draw_shape_layer as a generator
(ScreenRenderer.layer_shapes): Update because of the region
parameter change
(ExportRenderer.__init__): New. Extend the inherited constructor
with the destination region for the drawing
(ExportRenderer.RenderMap): Removed the map and region parameters
which are now in the constructor

* Thuban/UI/view.py (MapCanvas.PreviewBitmap): New. Return a
bitmap suitable for a preview in a tool
(CanvasPanTool.MouseMove): Use the PreviewBitmap method to get the
bitmap
(MapPrintout.draw_on_dc): Adapt to new renderer interface
(MapCanvas.OnPaint): Handle drawing the selection bitmap if it
exists
(MapCanvas.OnIdle): Update the logic to deal with incremental
rendering and the selection bitmap
(MapCanvas._do_redraw): Handle the instantiation of the render
iterator and the redraws during rendering
(MapCanvas._render_iterator): New. Generator to incrementally
redraw both bitmaps
(MapCanvas.Export): Adapt to new renderer interface.
(MapCanvas.full_redraw): Reset the selection bitmap and the
renderer iterator too
(MapCanvas.redraw_selection): New. Force a redraw of the selection
bitmap
(MapCanvas.shape_selected): Only redraw the selection bitmap

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26