/[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 2571 - (show annotations)
Fri Feb 18 14:54:17 2005 UTC (20 years ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 16053 byte(s)
Refactored baserenderer.py and renderer.py to remove baserenderer.py's
dependencies on wxPython. Added a new method projected_raster_layer()
that returns a raster layer image in projected space. This must be
implemented in classes derived from BaseRenderer. This also eliminates
the dependency on gdal in baserenderer.py.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26