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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26