/[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 2394 - (show annotations)
Wed Nov 17 22:02:29 2004 UTC (20 years, 3 months ago) by jan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 13457 byte(s)
(ScreenRenderer.draw_selection_incrementally):
Added consideration of the specific size of point symbols.
The property for each point symbol is retrieved and the size applied
for the rendering method.
Added doc-string.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26