/[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 2514 - (show annotations)
Mon Jan 3 16:21:17 2005 UTC (20 years, 2 months ago) by frank
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 13704 byte(s)
Thuban/UI/renderer.py (ScreenRendererdraw_selection_incrementally):
    BugFix 2883: Former implementation only worked on classified point layers:
    KeyError was raised, now use the default size if field is None.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26