/[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 2562 - (hide annotations)
Wed Feb 16 21:14:47 2005 UTC (20 years ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 15011 byte(s)
Further wxPython 2.5 changes using patches from Daniel Calvelo Aros
so that that wxproj doesn't crash. Added GUI support for selecting
alpha channel (opacity can't be selected yet).

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26