/[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 2602 - (hide annotations)
Mon Apr 25 15:01:48 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: 16934 byte(s)
Added a description according to the CVS log message

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 joey 2602 """Returns a raster layer image in projected space
121 jonathan 2571
122 joey 2602 Based on a given filename. This method must be implemented in
123     classes derived from BaseRenderer.
124     """
125    
126 jonathan 2571 ret = None
127    
128     if Thuban.Model.resource.has_gdal_support():
129    
130     if versions['wxPython-tuple'] < (2,5,3):
131     options = options | 4 # INVERT_MASK_BITS
132     options = options & ~2 # ALPHA_MASK not supported
133    
134     try:
135     ret = ProjectRasterFile(layer.GetImageFilename(),
136     srcProj, dstProj,
137     extents, resolution, dimensions,
138     options)
139     except (MemoryError, IOError, AttributeError, ValueError):
140     # Why does this catch AttributeError and ValueError?
141     # FIXME: The exception should be communicated to the user
142     # better.
143     traceback.print_exc()
144    
145     return ret
146    
147 jonathan 2587 def draw_raster_data(self, layer, x,y, data, format = 'BMP'):
148 jonathan 2551
149     mask = None
150 jonathan 2587 alpha = None
151 jonathan 2562 width = data[0]
152     height = data[1]
153     image_data, mask_data, alpha_data = data[2]
154 jonathan 2552
155 jonathan 2562 if versions['wxPython-tuple'] < (2,5,3):
156     alpha_data = None
157    
158 jonathan 2537 if format == 'RAW':
159 jonathan 2562 image = wxEmptyImage(width, height)
160     image.SetData(image_data)
161     if mask_data is not None:
162     mask = wxBitmapFromBits(mask_data, width, height, 1)
163 jonathan 2552 mask = wxMask(mask)
164 jonathan 2562 elif alpha_data is not None:
165 jonathan 2587 # alpha_data is already in the right format
166     alpha = alpha_data
167    
168 jonathan 2537 else:
169 jonathan 2562 stream = cStringIO.StringIO(image_data)
170 jonathan 2537 image = wxImageFromStream(stream, raster_format_map[format])
171 jonathan 2562 if mask_data is not None:
172     stream = cStringIO.StringIO(mask_data)
173 jonathan 2551 mask = wxImageFromStream(stream, raster_format_map[format])
174 jonathan 2552 mask = wxMask(wxBitmapFromImage(mask, 1))
175 jonathan 2562 elif alpha_data is not None:
176     stream = cStringIO.StringIO(alpha_data)
177     alpha = wxImageFromStream(stream, raster_format_map[format])
178 jonathan 2587 alpha = alpha.GetData()[:] # XXX: do we need to copy this?
179 jonathan 2537
180 jonathan 2587 #
181     # if we are using the alpha_data then scale down the alpha values
182     # by the layer's opacity using a string translation table
183     #
184     if alpha is not None:
185     lo = layer.Opacity()
186     if lo == 0:
187     return
188     elif lo == 1:
189     a = alpha
190     else:
191     tr = [int(i*lo) for i in range(256)]
192     table = array.array('B', tr).tostring()
193     a = alpha.translate(table)
194    
195     image.SetAlphaData(a)
196    
197 bh 1552 bitmap = wxBitmapFromImage(image)
198 jonathan 588
199 jonathan 2587 if mask is not None:
200     bitmap.SetMask(mask)
201    
202 jonathan 2552 self.dc.DrawBitmap(bitmap, int(round(x)), int(round(y)), True)
203 jonathan 588
204 jonathan 2537
205 bh 6 class ScreenRenderer(MapRenderer):
206    
207     # On the screen we want to see only visible layers by default
208     honor_visibility = 1
209 bh 1552
210 bh 1866 def RenderMap(self, selected_layer, selected_shapes):
211 bh 144 """Render the map.
212    
213 bh 148 Only the given region (a tuple in window coordinates as returned
214     by a wxrect's asTuple method) needs to be redrawn. Highlight the
215 bh 535 shapes given by the ids in selected_shapes in the
216     selected_layer.
217 bh 144 """
218 bh 6 self.selected_layer = selected_layer
219 bh 535 self.selected_shapes = selected_shapes
220 bh 1866 self.render_map()
221 bh 6
222 bh 1866 def RenderMapIncrementally(self):
223     """Render the map.
224 bh 535
225 bh 1866 Only the given region (a tuple in window coordinates as returned
226     by a wxrect's asTuple method) needs to be redrawn. Highlight the
227     shapes given by the ids in selected_shapes in the
228     selected_layer.
229     """
230     return self.render_map_incrementally()
231 bh 535
232 bh 1866 def draw_selection_incrementally(self, layer, selected_shapes):
233 jan 2394 """Draw the selected shapes in a emphasized way (i.e.
234     with a special pen and brush.
235     The drawing is performed incrementally, that means every
236     n shapes, the user can have interactions with the map.
237     n is currently fixed to 500.
238    
239     layer -- the layer where the shapes belong to.
240     selected_shapes -- a list of the shape-ids representing the
241     selected shapes for the given layer.
242     """
243 bh 1866 pen = wxPen(wxBLACK, 3, wxSOLID)
244     brush = wxBrush(wxBLACK, wxCROSS_HATCH)
245    
246     shapetype = layer.ShapeType()
247     useraw, func, param = self.low_level_renderer(layer)
248     args = (pen, brush)
249 jan 2394
250     # for point shapes we need to find out the properties
251     # to determine the size. Based on table and field,
252     # we can find out the properties for object - see below.
253     if shapetype == SHAPETYPE_POINT:
254     lc = layer.GetClassification()
255     field = layer.GetClassificationColumn()
256     table = layer.ShapeStore().Table()
257    
258 bh 1866 count = 0
259     for index in selected_shapes:
260     count += 1
261     shape = layer.Shape(index)
262 jan 2394
263     # Get the size of the specific property for this
264     # point
265 frank 2514 if shapetype == SHAPETYPE_POINT and field is not None:
266 jan 2394 value = table.ReadValue(shape.ShapeID(), field)
267     group = lc.FindGroup(value)
268     size = group.GetProperties().GetSize()
269     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     font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
383     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     dc.SetPen(wxBLACK_PEN)
395     dc.SetBrush(wxTRANSPARENT_BRUSH)
396    
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     dc.SetPen(wxBLACK_PEN)
421     dc.SetBrush(wxTRANSPARENT_BRUSH)
422    
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     dc.SetTextForeground(wxBLACK)
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     wxRect(posx+dx, posy,
453     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