/[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 2587 - (hide annotations)
Wed Mar 23 15:30:27 2005 UTC (19 years, 11 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 16730 byte(s)
Add support for adjusting the opacity of a raster layer.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26