/[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 2700 - (hide annotations)
Mon Sep 18 14:27:02 2006 UTC (18 years, 5 months ago) by dpinte
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 16780 byte(s)
2006-09-18 Didrik Pinte <dpinte@itae.be>
    
        * wxPython 2.6 update : wx 2.4 syntax has been updated to 2.6


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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26