/[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 2601 - (hide annotations)
Sat Apr 23 16:40:33 2005 UTC (19 years, 10 months ago) by joey
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 16748 byte(s)
Added a missing import

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26