/[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 1866 - (hide 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 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 bh 1866 from __future__ import generators
11    
12 bh 6 __version__ = "$Revision$"
13 bh 1866 # $Source$
14     # $Id$
15 bh 6
16 jonathan 938 import cStringIO
17    
18 jonathan 882 from Thuban import _
19    
20 bh 1552 from wxPython.wx import wxPoint, wxRect, wxPen, wxBrush, wxFont, \
21 frank 909 wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
22 bh 1552 wxBLACK_PEN, wxBLACK, wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
23     wxBitmapFromImage, wxImageFromStream, wxBITMAP_TYPE_BMP
24 bh 6
25 jonathan 676 from wxproj import draw_polygon_shape, draw_polygon_init
26 bh 6
27 jonathan 882 from Thuban.UI.common import Color2wxColour
28 frank 909 from Thuban.UI.classifier import ClassDataPreviewer
29     from Thuban.UI.scalebar import ScaleBar
30 jan 374
31 bh 1562 from Thuban.Model.data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \
32     SHAPETYPE_POINT, RAW_SHAPEFILE
33 bh 6
34 jonathan 1343 from Thuban.Model.color import Transparent
35 jonathan 1165 import Thuban.Model.resource
36 bh 6
37 bh 1552 from baserenderer import BaseRenderer
38 jonathan 1234
39 bh 1552 class MapRenderer(BaseRenderer):
40 bh 6
41     """Class to render a map onto a wxDC"""
42    
43 bh 1552 TRANSPARENT_PEN = wxTRANSPARENT_PEN
44     TRANSPARENT_BRUSH = wxTRANSPARENT_BRUSH
45 bh 6
46 bh 1552 make_point = wxPoint
47 bh 6
48 bh 1552 def tools_for_property(self, prop):
49     fill = prop.GetFill()
50     if fill is Transparent:
51     brush = self.TRANSPARENT_BRUSH
52 jonathan 798 else:
53 bh 1552 brush = wxBrush(Color2wxColour(fill), wxSOLID)
54 bh 1219
55 bh 1552 stroke = prop.GetLineColor()
56     if stroke is Transparent:
57     pen = self.TRANSPARENT_PEN
58 jonathan 938 else:
59 bh 1552 pen = wxPen(Color2wxColour(stroke), prop.GetLineWidth(), wxSOLID)
60     return pen, brush
61 jonathan 938
62 bh 1552 def low_level_renderer(self, layer):
63     """Override inherited method to provide more efficient renderers
64 bh 686
65 bh 1562 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 bh 144 """
73 bh 1562 if (layer.ShapeStore().RawShapeFormat() == RAW_SHAPEFILE
74     and layer.ShapeType() in (SHAPETYPE_ARC, SHAPETYPE_POLYGON)):
75 bh 1552 offx, offy = self.offset
76 bh 1591 return (True, draw_polygon_shape,
77 bh 1562 draw_polygon_init(layer.ShapeStore().Shapefile(),
78     self.dc, self.map.projection,
79 bh 1552 layer.projection,
80 bh 1562 self.scale, -self.scale, offx, offy))
81     else:
82     return BaseRenderer.low_level_renderer(self, layer)
83 bh 6
84 bh 1552 def label_font(self):
85     return wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
86 bh 6
87 bh 1552 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 jonathan 588
93    
94 bh 6 class ScreenRenderer(MapRenderer):
95    
96     # On the screen we want to see only visible layers by default
97     honor_visibility = 1
98 bh 1552
99 bh 1866 def RenderMap(self, selected_layer, selected_shapes):
100 bh 144 """Render the map.
101    
102 bh 148 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 bh 535 shapes given by the ids in selected_shapes in the
105     selected_layer.
106 bh 144 """
107 bh 6 self.selected_layer = selected_layer
108 bh 535 self.selected_shapes = selected_shapes
109 bh 1866 self.render_map()
110 bh 6
111 bh 1866 def RenderMapIncrementally(self):
112     """Render the map.
113 bh 535
114 bh 1866 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 bh 535
121 bh 1866 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 bh 1593 def layer_shapes(self, layer):
141 bh 144 """Return the shapeids covered by the region that has to be redrawn
142 bh 6
143 bh 144 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 bh 1866 x, y, width, height = self.region
162 bh 144 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 frank 909 class ExportRenderer(ScreenRenderer):
179 bh 6
180 frank 909 honor_visibility = 1
181 bh 1552
182 bh 1866 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 frank 909 """Render the map.
196 bh 6
197 frank 909 The rendering device has been specified during initialisation.
198 bh 1866 The device border distance was set in
199     Thuban.UI.viewport.output_transform().
200 bh 1552
201 frank 909 RenderMap renders a frame set (one page frame, one around
202 bh 1866 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 frank 909 """
207 bh 1552
208 frank 909 self.selected_layer = selected_layer
209     self.selected_shapes = selected_shapes
210    
211 bh 1552 # Get some dimensions
212 bh 1866 llx, lly, urx, ury = self.region
213     mminx, mminy, mmaxx, mmaxy = self.destination_region
214 frank 909
215 bh 1552 # Manipulate the offset to position the map
216 frank 909 offx, offy = self.offset
217     # 1. Shift to corner of map drawing area
218 bh 1552 offx = offx + mminx
219 frank 909 offy = offy + mminy
220    
221     # 2. Center the map on the map drawing area:
222 bh 1552 # region identifies the region on the canvas view:
223 frank 909 # 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 bh 1552 self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
233 frank 909 urx, ury)
234 bh 1866 self.render_map()
235 frank 909 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 bh 1866 self.render_frame()
246     self.render_legend()
247     self.render_scalebar()
248 frank 909 self.dc.EndDrawing()
249    
250 bh 1866 def render_frame(self):
251 frank 909 """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 bh 1866 mminx, mminy, mmaxx, mmaxy = self.destination_region
260 frank 909
261     # Page Frame
262     dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
263 bh 1552
264 frank 909 # Map Frame
265 bh 1866 llx, lly, urx, ury = self.region
266 frank 909 dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
267 bh 1552
268 frank 909 # 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 bh 1866 def render_legend(self):
276 frank 909 """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 bh 1866 mminx, mminy, mmaxx, mmaxy = self.destination_region
286 frank 909 textwidth, textheight = dc.GetTextExtent("0")
287     iconwidth = textheight
288     iconheight = textheight
289     stepy = textheight+3
290     dx = 10
291 bh 1552 posx = mmaxx + 10 + 5 # 10 pix distance mapframe/legend frame,
292 frank 909 # 5 pix inside legend frame
293     posy = mminy + 5 # 5 pix inside legend frame
294 bh 1552
295 frank 909 # Render the legend
296     dc.SetTextForeground(wxBLACK)
297 bh 1866 if self.map.HasLayers():
298     layers = self.map.Layers()[:]
299 frank 1324 layers.reverse()
300     for l in layers:
301 frank 909 if l.Visible():
302     # Render title
303     dc.DrawText(l.Title(), posx, posy)
304     posy+=stepy
305 jonathan 1298 if l.HasClassification():
306     # Render classification
307     clazz = l.GetClassification()
308     shapeType = l.ShapeType()
309     for g in clazz:
310     if g.IsVisible():
311 bh 1552 previewer.Draw(dc,
312     wxRect(posx+dx, posy,
313     iconwidth, iconheight),
314 jonathan 1298 g.GetProperties(), shapeType)
315 bh 1552 dc.DrawText(g.GetDisplayText(),
316 jonathan 1298 posx+2*dx+iconwidth, posy)
317     posy+=stepy
318 bh 1552
319 bh 1866 def render_scalebar(self):
320 frank 909 """Render the scalebar."""
321    
322 bh 1866 scalebar = ScaleBar(self.map)
323 frank 909
324     # Dimension stuff
325     width, height = self.dc.GetSizeTuple()
326 bh 1866 mminx, mminy, mmaxx, mmaxy = self.destination_region
327 bh 1552
328 frank 909 # Render the scalebar
329 bh 1552 scalebar.DrawScaleBar(self.scale, self.dc,
330 frank 909 (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