/[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 2639 - (hide annotations)
Thu Jun 30 16:15:03 2005 UTC (19 years, 8 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 17077 byte(s)
(ScreenRenderer.draw_selection_incrementally):
untabify.

1 jan 2635 # Copyright (c) 2001-2005 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 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 jan 2635 if shapetype == SHAPETYPE_POINT:
268 bh 2639 if field is not None:
269 jan 2635 value = table.ReadValue(shape.ShapeID(), field)
270     group = lc.FindGroup(value)
271     size = group.GetProperties().GetSize()
272     else:
273     size = lc.GetDefaultSize()
274 jan 2394 args = (pen, brush, size)
275    
276 bh 1866 if useraw:
277     data = shape.RawData()
278     else:
279     data = shape.Points()
280     func(param, data, *args)
281     if count % 500 == 0:
282     yield True
283    
284 bh 1593 def layer_shapes(self, layer):
285 bh 144 """Return the shapeids covered by the region that has to be redrawn
286 bh 6
287 bh 144 Call the layer's ShapesInRegion method to determine the ids so
288     that it can use the quadtree.
289     """
290     # FIXME: the quad-tree should be built from the projected
291     # coordinates not the lat-long ones because it's not trivial to
292     # determine an appropriate rectangle in lat-long for a given
293     # rectangle in projected coordinates which we have to start from
294     # here.
295     proj = self.map.projection
296     if proj is not None:
297     inverse = proj.Inverse
298     else:
299     inverse = None
300    
301     scale = self.scale
302     offx, offy = self.offset
303     xs = []
304     ys = []
305 bh 1866 x, y, width, height = self.region
306 bh 144 for winx, winy in ((x, y), (x + width, y),
307     (x + width, y + height), (x, y + height)):
308     px = (winx - offx) / scale
309     py = -(winy - offy) / scale
310     if inverse:
311     px, py = inverse(px, py)
312     xs.append(px)
313     ys.append(py)
314     left = min(xs)
315     right = max(xs)
316     top = max(ys)
317     bottom = min(ys)
318    
319     return layer.ShapesInRegion((left, bottom, right, top))
320    
321    
322 frank 909 class ExportRenderer(ScreenRenderer):
323 bh 6
324 frank 909 honor_visibility = 1
325 bh 1552
326 bh 1866 def __init__(self, *args, **kw):
327     """Initialize the ExportRenderer.
328    
329     In addition to all parameters of the the ScreenRender
330     constructor, this class requires and additional keyword argument
331     destination_region with a tuple (minx, miny, maxx, maxy) giving
332     the region in dc coordinates which is to contain the map.
333     """
334     self.destination_region = kw["destination_region"]
335     del kw["destination_region"]
336     ScreenRenderer.__init__(self, *args, **kw)
337    
338     def RenderMap(self, selected_layer, selected_shapes):
339 frank 909 """Render the map.
340 bh 6
341 frank 909 The rendering device has been specified during initialisation.
342 bh 1866 The device border distance was set in
343     Thuban.UI.viewport.output_transform().
344 bh 1552
345 frank 909 RenderMap renders a frame set (one page frame, one around
346 bh 1866 legend/scalebar and one around the map), the map, the legend and
347     the scalebar on the given DC. The map is rendered with the
348     region displayed in the canvas view, centered on the area
349     available for map display.
350 frank 909 """
351 bh 1552
352 frank 909 self.selected_layer = selected_layer
353     self.selected_shapes = selected_shapes
354    
355 bh 1552 # Get some dimensions
356 bh 1866 llx, lly, urx, ury = self.region
357     mminx, mminy, mmaxx, mmaxy = self.destination_region
358 frank 909
359 bh 1552 # Manipulate the offset to position the map
360 frank 909 offx, offy = self.offset
361     # 1. Shift to corner of map drawing area
362 bh 1552 offx = offx + mminx
363 frank 909 offy = offy + mminy
364    
365     # 2. Center the map on the map drawing area:
366 bh 1552 # region identifies the region on the canvas view:
367 frank 909 # center of map drawing area - half the size of region: rendering origin
368     self.shiftx = (mmaxx - mminx)*0.5 - (urx - llx)*0.5
369     self.shifty = (mmaxy - mminy)*0.5 - (ury - lly)*0.5
370    
371     self.offset = (offx+self.shiftx, offy+self.shifty)
372 bh 2454 self.region = (llx + self.shiftx, lly + self.shifty, urx, ury)
373 frank 909
374     # Draw the map
375     self.dc.BeginDrawing()
376     self.dc.DestroyClippingRegion()
377 bh 1552 self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
378 frank 909 urx, ury)
379 bh 1866 self.render_map()
380 frank 909 self.dc.EndDrawing()
381    
382     # Draw the rest (frames, legend, scalebar)
383     self.dc.BeginDrawing()
384     self.dc.DestroyClippingRegion()
385    
386     # Force the font for Legend drawing
387     font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
388     self.dc.SetFont(font)
389    
390 bh 1866 self.render_frame()
391     self.render_legend()
392     self.render_scalebar()
393 frank 909 self.dc.EndDrawing()
394    
395 bh 1866 def render_frame(self):
396 frank 909 """Render the frames for map and legend/scalebar."""
397    
398     dc = self.dc
399     dc.SetPen(wxBLACK_PEN)
400     dc.SetBrush(wxTRANSPARENT_BRUSH)
401    
402     # Dimension stuff
403     width, height = dc.GetSizeTuple()
404 bh 1866 mminx, mminy, mmaxx, mmaxy = self.destination_region
405 frank 909
406     # Page Frame
407     dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
408 bh 1552
409 frank 909 # Map Frame
410 bh 1866 llx, lly, urx, ury = self.region
411 frank 909 dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
412 bh 1552
413 frank 909 # Legend Frame
414     dc.DrawRectangle(mmaxx+10,mminy,(width-20) - (mmaxx+10), mmaxy-mminy)
415    
416     dc.DestroyClippingRegion()
417     dc.SetClippingRegion(mmaxx+10,mminy,
418     (width-20) - (mmaxx+10), mmaxy-mminy)
419    
420 bh 1866 def render_legend(self):
421 frank 909 """Render the legend on the Map."""
422    
423     previewer = ClassDataPreviewer()
424     dc = self.dc
425     dc.SetPen(wxBLACK_PEN)
426     dc.SetBrush(wxTRANSPARENT_BRUSH)
427    
428     # Dimension stuff
429     width, height = dc.GetSizeTuple()
430 bh 1866 mminx, mminy, mmaxx, mmaxy = self.destination_region
431 frank 909 textwidth, textheight = dc.GetTextExtent("0")
432     iconwidth = textheight
433     iconheight = textheight
434     stepy = textheight+3
435     dx = 10
436 bh 1552 posx = mmaxx + 10 + 5 # 10 pix distance mapframe/legend frame,
437 frank 909 # 5 pix inside legend frame
438     posy = mminy + 5 # 5 pix inside legend frame
439 bh 1552
440 frank 909 # Render the legend
441     dc.SetTextForeground(wxBLACK)
442 bh 1866 if self.map.HasLayers():
443     layers = self.map.Layers()[:]
444 frank 1324 layers.reverse()
445     for l in layers:
446 frank 909 if l.Visible():
447     # Render title
448     dc.DrawText(l.Title(), posx, posy)
449     posy+=stepy
450 jonathan 1298 if l.HasClassification():
451     # Render classification
452     clazz = l.GetClassification()
453     shapeType = l.ShapeType()
454     for g in clazz:
455     if g.IsVisible():
456 bh 1552 previewer.Draw(dc,
457     wxRect(posx+dx, posy,
458     iconwidth, iconheight),
459 jonathan 1298 g.GetProperties(), shapeType)
460 bh 1552 dc.DrawText(g.GetDisplayText(),
461 jonathan 1298 posx+2*dx+iconwidth, posy)
462     posy+=stepy
463 bh 1552
464 bh 1866 def render_scalebar(self):
465 frank 909 """Render the scalebar."""
466    
467 bh 1866 scalebar = ScaleBar(self.map)
468 frank 909
469     # Dimension stuff
470     width, height = self.dc.GetSizeTuple()
471 bh 1866 mminx, mminy, mmaxx, mmaxy = self.destination_region
472 bh 1552
473 frank 909 # Render the scalebar
474 bh 1552 scalebar.DrawScaleBar(self.scale, self.dc,
475 frank 909 (mmaxx+10+5, mmaxy-25),
476     ((width-15-5) - (mmaxx+10+5),20)
477     )
478     # 10 pix between map and legend frame, 5 pix inside legend frame
479     # 25 pix from the legend frame bottom line
480     # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
481     # Height: 20
482    
483     class PrinterRenderer(ExportRenderer):
484    
485     # Printing as well as Export / Screen display only the visible layer.
486     honor_visibility = 1
487    

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26