/[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 2618 - (hide annotations)
Fri May 6 14:18:31 2005 UTC (19 years, 10 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 16981 byte(s)
(MapRenderer.draw_raster_data): Fixed signature to match that in BaseRenderer.
Use the new opacity argument in place of calling layer.Opacity(). In the case
where the format is not 'RAW', alpha_data is None and the loaded image has
alpha information, use the file's alpha information. This is still subject to
the layer's opacity setting.

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 2618 def draw_raster_data(self, x,y, data, format = 'BMP', opacity=1.0):
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 2618
172 jonathan 2562 if mask_data is not None:
173     stream = cStringIO.StringIO(mask_data)
174 jonathan 2551 mask = wxImageFromStream(stream, raster_format_map[format])
175 jonathan 2552 mask = wxMask(wxBitmapFromImage(mask, 1))
176 jonathan 2562 elif alpha_data is not None:
177     stream = cStringIO.StringIO(alpha_data)
178     alpha = wxImageFromStream(stream, raster_format_map[format])
179 jonathan 2618 alpha = alpha.GetData() #[:] # XXX: do we need to copy this?
180     elif image.HasAlpha():
181     alpha = image.GetAlphaData()
182 jonathan 2537
183 jonathan 2587 #
184 jonathan 2618 # scale down the alpha values the opacity level using a string
185     # translation table for efficiency.
186 jonathan 2587 #
187     if alpha is not None:
188 jonathan 2618 if opacity == 0:
189 jonathan 2587 return
190 jonathan 2618 elif opacity == 1:
191 jonathan 2587 a = alpha
192     else:
193 jonathan 2618 tr = [int(i*opacity) for i in range(256)]
194 jonathan 2587 table = array.array('B', tr).tostring()
195     a = alpha.translate(table)
196    
197     image.SetAlphaData(a)
198    
199 bh 1552 bitmap = wxBitmapFromImage(image)
200 jonathan 588
201 jonathan 2587 if mask is not None:
202     bitmap.SetMask(mask)
203    
204 jonathan 2552 self.dc.DrawBitmap(bitmap, int(round(x)), int(round(y)), True)
205 jonathan 588
206 jonathan 2537
207 bh 6 class ScreenRenderer(MapRenderer):
208    
209     # On the screen we want to see only visible layers by default
210     honor_visibility = 1
211 bh 1552
212 bh 1866 def RenderMap(self, selected_layer, selected_shapes):
213 bh 144 """Render the map.
214    
215 bh 148 Only the given region (a tuple in window coordinates as returned
216     by a wxrect's asTuple method) needs to be redrawn. Highlight the
217 bh 535 shapes given by the ids in selected_shapes in the
218     selected_layer.
219 bh 144 """
220 bh 6 self.selected_layer = selected_layer
221 bh 535 self.selected_shapes = selected_shapes
222 bh 1866 self.render_map()
223 bh 6
224 bh 1866 def RenderMapIncrementally(self):
225     """Render the map.
226 bh 535
227 bh 1866 Only the given region (a tuple in window coordinates as returned
228     by a wxrect's asTuple method) needs to be redrawn. Highlight the
229     shapes given by the ids in selected_shapes in the
230     selected_layer.
231     """
232     return self.render_map_incrementally()
233 bh 535
234 bh 1866 def draw_selection_incrementally(self, layer, selected_shapes):
235 jan 2394 """Draw the selected shapes in a emphasized way (i.e.
236     with a special pen and brush.
237     The drawing is performed incrementally, that means every
238     n shapes, the user can have interactions with the map.
239     n is currently fixed to 500.
240    
241     layer -- the layer where the shapes belong to.
242     selected_shapes -- a list of the shape-ids representing the
243     selected shapes for the given layer.
244     """
245 bh 1866 pen = wxPen(wxBLACK, 3, wxSOLID)
246     brush = wxBrush(wxBLACK, wxCROSS_HATCH)
247    
248     shapetype = layer.ShapeType()
249     useraw, func, param = self.low_level_renderer(layer)
250     args = (pen, brush)
251 jan 2394
252     # for point shapes we need to find out the properties
253     # to determine the size. Based on table and field,
254     # we can find out the properties for object - see below.
255     if shapetype == SHAPETYPE_POINT:
256     lc = layer.GetClassification()
257     field = layer.GetClassificationColumn()
258     table = layer.ShapeStore().Table()
259    
260 bh 1866 count = 0
261     for index in selected_shapes:
262     count += 1
263     shape = layer.Shape(index)
264 jan 2394
265     # Get the size of the specific property for this
266     # point
267 frank 2514 if shapetype == SHAPETYPE_POINT and field is not None:
268 jan 2394 value = table.ReadValue(shape.ShapeID(), field)
269     group = lc.FindGroup(value)
270     size = group.GetProperties().GetSize()
271     args = (pen, brush, size)
272    
273 bh 1866 if useraw:
274     data = shape.RawData()
275     else:
276     data = shape.Points()
277     func(param, data, *args)
278     if count % 500 == 0:
279     yield True
280    
281 bh 1593 def layer_shapes(self, layer):
282 bh 144 """Return the shapeids covered by the region that has to be redrawn
283 bh 6
284 bh 144 Call the layer's ShapesInRegion method to determine the ids so
285     that it can use the quadtree.
286     """
287     # FIXME: the quad-tree should be built from the projected
288     # coordinates not the lat-long ones because it's not trivial to
289     # determine an appropriate rectangle in lat-long for a given
290     # rectangle in projected coordinates which we have to start from
291     # here.
292     proj = self.map.projection
293     if proj is not None:
294     inverse = proj.Inverse
295     else:
296     inverse = None
297    
298     scale = self.scale
299     offx, offy = self.offset
300     xs = []
301     ys = []
302 bh 1866 x, y, width, height = self.region
303 bh 144 for winx, winy in ((x, y), (x + width, y),
304     (x + width, y + height), (x, y + height)):
305     px = (winx - offx) / scale
306     py = -(winy - offy) / scale
307     if inverse:
308     px, py = inverse(px, py)
309     xs.append(px)
310     ys.append(py)
311     left = min(xs)
312     right = max(xs)
313     top = max(ys)
314     bottom = min(ys)
315    
316     return layer.ShapesInRegion((left, bottom, right, top))
317    
318    
319 frank 909 class ExportRenderer(ScreenRenderer):
320 bh 6
321 frank 909 honor_visibility = 1
322 bh 1552
323 bh 1866 def __init__(self, *args, **kw):
324     """Initialize the ExportRenderer.
325    
326     In addition to all parameters of the the ScreenRender
327     constructor, this class requires and additional keyword argument
328     destination_region with a tuple (minx, miny, maxx, maxy) giving
329     the region in dc coordinates which is to contain the map.
330     """
331     self.destination_region = kw["destination_region"]
332     del kw["destination_region"]
333     ScreenRenderer.__init__(self, *args, **kw)
334    
335     def RenderMap(self, selected_layer, selected_shapes):
336 frank 909 """Render the map.
337 bh 6
338 frank 909 The rendering device has been specified during initialisation.
339 bh 1866 The device border distance was set in
340     Thuban.UI.viewport.output_transform().
341 bh 1552
342 frank 909 RenderMap renders a frame set (one page frame, one around
343 bh 1866 legend/scalebar and one around the map), the map, the legend and
344     the scalebar on the given DC. The map is rendered with the
345     region displayed in the canvas view, centered on the area
346     available for map display.
347 frank 909 """
348 bh 1552
349 frank 909 self.selected_layer = selected_layer
350     self.selected_shapes = selected_shapes
351    
352 bh 1552 # Get some dimensions
353 bh 1866 llx, lly, urx, ury = self.region
354     mminx, mminy, mmaxx, mmaxy = self.destination_region
355 frank 909
356 bh 1552 # Manipulate the offset to position the map
357 frank 909 offx, offy = self.offset
358     # 1. Shift to corner of map drawing area
359 bh 1552 offx = offx + mminx
360 frank 909 offy = offy + mminy
361    
362     # 2. Center the map on the map drawing area:
363 bh 1552 # region identifies the region on the canvas view:
364 frank 909 # center of map drawing area - half the size of region: rendering origin
365     self.shiftx = (mmaxx - mminx)*0.5 - (urx - llx)*0.5
366     self.shifty = (mmaxy - mminy)*0.5 - (ury - lly)*0.5
367    
368     self.offset = (offx+self.shiftx, offy+self.shifty)
369 bh 2454 self.region = (llx + self.shiftx, lly + self.shifty, urx, ury)
370 frank 909
371     # Draw the map
372     self.dc.BeginDrawing()
373     self.dc.DestroyClippingRegion()
374 bh 1552 self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
375 frank 909 urx, ury)
376 bh 1866 self.render_map()
377 frank 909 self.dc.EndDrawing()
378    
379     # Draw the rest (frames, legend, scalebar)
380     self.dc.BeginDrawing()
381     self.dc.DestroyClippingRegion()
382    
383     # Force the font for Legend drawing
384     font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
385     self.dc.SetFont(font)
386    
387 bh 1866 self.render_frame()
388     self.render_legend()
389     self.render_scalebar()
390 frank 909 self.dc.EndDrawing()
391    
392 bh 1866 def render_frame(self):
393 frank 909 """Render the frames for map and legend/scalebar."""
394    
395     dc = self.dc
396     dc.SetPen(wxBLACK_PEN)
397     dc.SetBrush(wxTRANSPARENT_BRUSH)
398    
399     # Dimension stuff
400     width, height = dc.GetSizeTuple()
401 bh 1866 mminx, mminy, mmaxx, mmaxy = self.destination_region
402 frank 909
403     # Page Frame
404     dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
405 bh 1552
406 frank 909 # Map Frame
407 bh 1866 llx, lly, urx, ury = self.region
408 frank 909 dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
409 bh 1552
410 frank 909 # Legend Frame
411     dc.DrawRectangle(mmaxx+10,mminy,(width-20) - (mmaxx+10), mmaxy-mminy)
412    
413     dc.DestroyClippingRegion()
414     dc.SetClippingRegion(mmaxx+10,mminy,
415     (width-20) - (mmaxx+10), mmaxy-mminy)
416    
417 bh 1866 def render_legend(self):
418 frank 909 """Render the legend on the Map."""
419    
420     previewer = ClassDataPreviewer()
421     dc = self.dc
422     dc.SetPen(wxBLACK_PEN)
423     dc.SetBrush(wxTRANSPARENT_BRUSH)
424    
425     # Dimension stuff
426     width, height = dc.GetSizeTuple()
427 bh 1866 mminx, mminy, mmaxx, mmaxy = self.destination_region
428 frank 909 textwidth, textheight = dc.GetTextExtent("0")
429     iconwidth = textheight
430     iconheight = textheight
431     stepy = textheight+3
432     dx = 10
433 bh 1552 posx = mmaxx + 10 + 5 # 10 pix distance mapframe/legend frame,
434 frank 909 # 5 pix inside legend frame
435     posy = mminy + 5 # 5 pix inside legend frame
436 bh 1552
437 frank 909 # Render the legend
438     dc.SetTextForeground(wxBLACK)
439 bh 1866 if self.map.HasLayers():
440     layers = self.map.Layers()[:]
441 frank 1324 layers.reverse()
442     for l in layers:
443 frank 909 if l.Visible():
444     # Render title
445     dc.DrawText(l.Title(), posx, posy)
446     posy+=stepy
447 jonathan 1298 if l.HasClassification():
448     # Render classification
449     clazz = l.GetClassification()
450     shapeType = l.ShapeType()
451     for g in clazz:
452     if g.IsVisible():
453 bh 1552 previewer.Draw(dc,
454     wxRect(posx+dx, posy,
455     iconwidth, iconheight),
456 jonathan 1298 g.GetProperties(), shapeType)
457 bh 1552 dc.DrawText(g.GetDisplayText(),
458 jonathan 1298 posx+2*dx+iconwidth, posy)
459     posy+=stepy
460 bh 1552
461 bh 1866 def render_scalebar(self):
462 frank 909 """Render the scalebar."""
463    
464 bh 1866 scalebar = ScaleBar(self.map)
465 frank 909
466     # Dimension stuff
467     width, height = self.dc.GetSizeTuple()
468 bh 1866 mminx, mminy, mmaxx, mmaxy = self.destination_region
469 bh 1552
470 frank 909 # Render the scalebar
471 bh 1552 scalebar.DrawScaleBar(self.scale, self.dc,
472 frank 909 (mmaxx+10+5, mmaxy-25),
473     ((width-15-5) - (mmaxx+10+5),20)
474     )
475     # 10 pix between map and legend frame, 5 pix inside legend frame
476     # 25 pix from the legend frame bottom line
477     # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
478     # Height: 20
479    
480     class PrinterRenderer(ExportRenderer):
481    
482     # Printing as well as Export / Screen display only the visible layer.
483     honor_visibility = 1
484    

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26