/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/renderer.py
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/Thuban/UI/renderer.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2551 - (show 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 # Copyright (c) 2001-2004 by Intevation GmbH
2 # Authors:
3 # 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 #
8 # This program is free software under the GPL (>=v2)
9 # Read the file COPYING coming with Thuban for details.
10
11 from __future__ import generators
12
13 __version__ = "$Revision$"
14 # $Source$
15 # $Id$
16
17 import cStringIO
18
19 from Thuban import _
20
21 from wxPython.wx import wxPoint, wxRect, wxPen, wxBrush, wxFont, \
22 wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
23 wxBLACK_PEN, wxBLACK, wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
24 wxBitmapFromImage, wxImageFromStream, wxBITMAP_TYPE_BMP, \
25 wxBITMAP_TYPE_JPEG, wxBITMAP_TYPE_PNG, wxBITMAP_TYPE_TIF, \
26 wxBITMAP_TYPE_GIF, wxEmptyImage, wxMask
27
28 from wxproj import draw_polygon_shape, draw_polygon_init
29
30 from Thuban.UI.common import Color2wxColour
31 from Thuban.UI.classifier import ClassDataPreviewer
32 from Thuban.UI.scalebar import ScaleBar
33
34 from Thuban.Model.data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \
35 SHAPETYPE_POINT, RAW_SHAPEFILE
36
37 from Thuban.Model.color import Transparent
38 import Thuban.Model.resource
39
40 from baserenderer import BaseRenderer
41
42 from math import floor
43
44 from types import StringType
45
46
47 # 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 "PNG": wxBITMAP_TYPE_PNG,
53 "TIFF": wxBITMAP_TYPE_TIF,
54 "GIF": wxBITMAP_TYPE_GIF,
55 }
56
57 class MapRenderer(BaseRenderer):
58
59 """Class to render a map onto a wxDC"""
60
61 TRANSPARENT_PEN = wxTRANSPARENT_PEN
62 TRANSPARENT_BRUSH = wxTRANSPARENT_BRUSH
63
64 def make_point(self, x, y):
65 return wxPoint(int(round(x)), int(round(y)))
66
67 def tools_for_property(self, prop):
68 fill = prop.GetFill()
69 if fill is Transparent:
70 brush = self.TRANSPARENT_BRUSH
71 else:
72 brush = wxBrush(Color2wxColour(fill), wxSOLID)
73
74 stroke = prop.GetLineColor()
75 if stroke is Transparent:
76 pen = self.TRANSPARENT_PEN
77 else:
78 pen = wxPen(Color2wxColour(stroke), prop.GetLineWidth(), wxSOLID)
79 return pen, brush
80
81 def low_level_renderer(self, layer):
82 """Override inherited method to provide more efficient renderers
83
84 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 """
92 if (layer.ShapeStore().RawShapeFormat() == RAW_SHAPEFILE
93 and layer.ShapeType() in (SHAPETYPE_ARC, SHAPETYPE_POLYGON)):
94 offx, offy = self.offset
95 return (True, draw_polygon_shape,
96 draw_polygon_init(layer.ShapeStore().Shapefile(),
97 self.dc, self.map.projection,
98 layer.projection,
99 self.scale, -self.scale, offx, offy))
100 else:
101 return BaseRenderer.low_level_renderer(self, layer)
102
103 def label_font(self):
104 return wxFont(int(round(self.resolution * 10)), wxSWISS, wxNORMAL,
105 wxNORMAL)
106
107 def draw_raster_data(self, x,y, data, format = 'BMP'):
108
109 mask = None
110 if format == 'RAW':
111 image = wxEmptyImage(data[0], data[1])
112 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 else:
117 stream = cStringIO.StringIO(data[2][0])
118 image = wxImageFromStream(stream, raster_format_map[format])
119 if data[2][1] is not None:
120 stream = cStringIO.StringIO(data[2][1])
121 mask = wxImageFromStream(stream, raster_format_map[format])
122
123 bitmap = wxBitmapFromImage(image)
124
125 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 bitmap.SetMask(wxMask(wxBitmapFromImage(mask, 1)))
132 self.dc.DrawBitmap(bitmap, int(round(x)), int(round(y)), True)
133 except (TypeError):
134 # implement using a mask image
135 raise NotImplementedError
136
137
138 class ScreenRenderer(MapRenderer):
139
140 # On the screen we want to see only visible layers by default
141 honor_visibility = 1
142
143 def RenderMap(self, selected_layer, selected_shapes):
144 """Render the map.
145
146 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 shapes given by the ids in selected_shapes in the
149 selected_layer.
150 """
151 self.selected_layer = selected_layer
152 self.selected_shapes = selected_shapes
153 self.render_map()
154
155 def RenderMapIncrementally(self):
156 """Render the map.
157
158 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
165 def draw_selection_incrementally(self, layer, selected_shapes):
166 """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 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
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 count = 0
192 for index in selected_shapes:
193 count += 1
194 shape = layer.Shape(index)
195
196 # Get the size of the specific property for this
197 # point
198 if shapetype == SHAPETYPE_POINT and field is not None:
199 value = table.ReadValue(shape.ShapeID(), field)
200 group = lc.FindGroup(value)
201 size = group.GetProperties().GetSize()
202 args = (pen, brush, size)
203
204 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 def layer_shapes(self, layer):
213 """Return the shapeids covered by the region that has to be redrawn
214
215 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 x, y, width, height = self.region
234 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 class ExportRenderer(ScreenRenderer):
251
252 honor_visibility = 1
253
254 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 """Render the map.
268
269 The rendering device has been specified during initialisation.
270 The device border distance was set in
271 Thuban.UI.viewport.output_transform().
272
273 RenderMap renders a frame set (one page frame, one around
274 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 """
279
280 self.selected_layer = selected_layer
281 self.selected_shapes = selected_shapes
282
283 # Get some dimensions
284 llx, lly, urx, ury = self.region
285 mminx, mminy, mmaxx, mmaxy = self.destination_region
286
287 # Manipulate the offset to position the map
288 offx, offy = self.offset
289 # 1. Shift to corner of map drawing area
290 offx = offx + mminx
291 offy = offy + mminy
292
293 # 2. Center the map on the map drawing area:
294 # region identifies the region on the canvas view:
295 # 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 self.region = (llx + self.shiftx, lly + self.shifty, urx, ury)
301
302 # Draw the map
303 self.dc.BeginDrawing()
304 self.dc.DestroyClippingRegion()
305 self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
306 urx, ury)
307 self.render_map()
308 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 self.render_frame()
319 self.render_legend()
320 self.render_scalebar()
321 self.dc.EndDrawing()
322
323 def render_frame(self):
324 """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 mminx, mminy, mmaxx, mmaxy = self.destination_region
333
334 # Page Frame
335 dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
336
337 # Map Frame
338 llx, lly, urx, ury = self.region
339 dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
340
341 # 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 def render_legend(self):
349 """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 mminx, mminy, mmaxx, mmaxy = self.destination_region
359 textwidth, textheight = dc.GetTextExtent("0")
360 iconwidth = textheight
361 iconheight = textheight
362 stepy = textheight+3
363 dx = 10
364 posx = mmaxx + 10 + 5 # 10 pix distance mapframe/legend frame,
365 # 5 pix inside legend frame
366 posy = mminy + 5 # 5 pix inside legend frame
367
368 # Render the legend
369 dc.SetTextForeground(wxBLACK)
370 if self.map.HasLayers():
371 layers = self.map.Layers()[:]
372 layers.reverse()
373 for l in layers:
374 if l.Visible():
375 # Render title
376 dc.DrawText(l.Title(), posx, posy)
377 posy+=stepy
378 if l.HasClassification():
379 # Render classification
380 clazz = l.GetClassification()
381 shapeType = l.ShapeType()
382 for g in clazz:
383 if g.IsVisible():
384 previewer.Draw(dc,
385 wxRect(posx+dx, posy,
386 iconwidth, iconheight),
387 g.GetProperties(), shapeType)
388 dc.DrawText(g.GetDisplayText(),
389 posx+2*dx+iconwidth, posy)
390 posy+=stepy
391
392 def render_scalebar(self):
393 """Render the scalebar."""
394
395 scalebar = ScaleBar(self.map)
396
397 # Dimension stuff
398 width, height = self.dc.GetSizeTuple()
399 mminx, mminy, mmaxx, mmaxy = self.destination_region
400
401 # Render the scalebar
402 scalebar.DrawScaleBar(self.scale, self.dc,
403 (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