/[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 2571 - (hide annotations)
Fri Feb 18 14:54:17 2005 UTC (20 years ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 16053 byte(s)
Refactored baserenderer.py and renderer.py to remove baserenderer.py's
dependencies on wxPython. Added a new method projected_raster_layer()
that returns a raster layer image in projected space. This must be
implemented in classes derived from BaseRenderer. This also eliminates
the dependency on gdal in baserenderer.py.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26