/[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 2551 - (hide annotations)
Thu Jan 27 14:19:41 2005 UTC (20 years, 1 month ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 14749 byte(s)
Add a new dialog box for raster layers. The dialog box allows
the user to toggle a mask that is generated by ProjectRasterFile
and is used to only draw the real parts of the projected image.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26