/[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 2552 - (show annotations)
Fri Jan 28 15:54:00 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: 14414 byte(s)
Make layer's use_mask flag default to true. Support a bit array describing
the mask to use. Improve error handling in ProjectRasterFile (also addresses
RT #2947).

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, wxBitmapFromBits
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
111 if format == 'RAW':
112 image = wxEmptyImage(data[0], data[1])
113 image.SetData(data[2][0])
114 if data[2][1] is not None:
115 mask = wxBitmapFromBits(data[2][1], data[0], data[1], 1)
116 mask = wxMask(mask)
117 else:
118 stream = cStringIO.StringIO(data[2][0])
119 image = wxImageFromStream(stream, raster_format_map[format])
120 if data[2][1] is not None:
121 stream = cStringIO.StringIO(data[2][1])
122 mask = wxImageFromStream(stream, raster_format_map[format])
123 mask = wxMask(wxBitmapFromImage(mask, 1))
124
125 bitmap = wxBitmapFromImage(image)
126 bitmap.SetMask(mask)
127
128 self.dc.DrawBitmap(bitmap, int(round(x)), int(round(y)), True)
129
130
131 class ScreenRenderer(MapRenderer):
132
133 # On the screen we want to see only visible layers by default
134 honor_visibility = 1
135
136 def RenderMap(self, selected_layer, selected_shapes):
137 """Render the map.
138
139 Only the given region (a tuple in window coordinates as returned
140 by a wxrect's asTuple method) needs to be redrawn. Highlight the
141 shapes given by the ids in selected_shapes in the
142 selected_layer.
143 """
144 self.selected_layer = selected_layer
145 self.selected_shapes = selected_shapes
146 self.render_map()
147
148 def RenderMapIncrementally(self):
149 """Render the map.
150
151 Only the given region (a tuple in window coordinates as returned
152 by a wxrect's asTuple method) needs to be redrawn. Highlight the
153 shapes given by the ids in selected_shapes in the
154 selected_layer.
155 """
156 return self.render_map_incrementally()
157
158 def draw_selection_incrementally(self, layer, selected_shapes):
159 """Draw the selected shapes in a emphasized way (i.e.
160 with a special pen and brush.
161 The drawing is performed incrementally, that means every
162 n shapes, the user can have interactions with the map.
163 n is currently fixed to 500.
164
165 layer -- the layer where the shapes belong to.
166 selected_shapes -- a list of the shape-ids representing the
167 selected shapes for the given layer.
168 """
169 pen = wxPen(wxBLACK, 3, wxSOLID)
170 brush = wxBrush(wxBLACK, wxCROSS_HATCH)
171
172 shapetype = layer.ShapeType()
173 useraw, func, param = self.low_level_renderer(layer)
174 args = (pen, brush)
175
176 # for point shapes we need to find out the properties
177 # to determine the size. Based on table and field,
178 # we can find out the properties for object - see below.
179 if shapetype == SHAPETYPE_POINT:
180 lc = layer.GetClassification()
181 field = layer.GetClassificationColumn()
182 table = layer.ShapeStore().Table()
183
184 count = 0
185 for index in selected_shapes:
186 count += 1
187 shape = layer.Shape(index)
188
189 # Get the size of the specific property for this
190 # point
191 if shapetype == SHAPETYPE_POINT and field is not None:
192 value = table.ReadValue(shape.ShapeID(), field)
193 group = lc.FindGroup(value)
194 size = group.GetProperties().GetSize()
195 args = (pen, brush, size)
196
197 if useraw:
198 data = shape.RawData()
199 else:
200 data = shape.Points()
201 func(param, data, *args)
202 if count % 500 == 0:
203 yield True
204
205 def layer_shapes(self, layer):
206 """Return the shapeids covered by the region that has to be redrawn
207
208 Call the layer's ShapesInRegion method to determine the ids so
209 that it can use the quadtree.
210 """
211 # FIXME: the quad-tree should be built from the projected
212 # coordinates not the lat-long ones because it's not trivial to
213 # determine an appropriate rectangle in lat-long for a given
214 # rectangle in projected coordinates which we have to start from
215 # here.
216 proj = self.map.projection
217 if proj is not None:
218 inverse = proj.Inverse
219 else:
220 inverse = None
221
222 scale = self.scale
223 offx, offy = self.offset
224 xs = []
225 ys = []
226 x, y, width, height = self.region
227 for winx, winy in ((x, y), (x + width, y),
228 (x + width, y + height), (x, y + height)):
229 px = (winx - offx) / scale
230 py = -(winy - offy) / scale
231 if inverse:
232 px, py = inverse(px, py)
233 xs.append(px)
234 ys.append(py)
235 left = min(xs)
236 right = max(xs)
237 top = max(ys)
238 bottom = min(ys)
239
240 return layer.ShapesInRegion((left, bottom, right, top))
241
242
243 class ExportRenderer(ScreenRenderer):
244
245 honor_visibility = 1
246
247 def __init__(self, *args, **kw):
248 """Initialize the ExportRenderer.
249
250 In addition to all parameters of the the ScreenRender
251 constructor, this class requires and additional keyword argument
252 destination_region with a tuple (minx, miny, maxx, maxy) giving
253 the region in dc coordinates which is to contain the map.
254 """
255 self.destination_region = kw["destination_region"]
256 del kw["destination_region"]
257 ScreenRenderer.__init__(self, *args, **kw)
258
259 def RenderMap(self, selected_layer, selected_shapes):
260 """Render the map.
261
262 The rendering device has been specified during initialisation.
263 The device border distance was set in
264 Thuban.UI.viewport.output_transform().
265
266 RenderMap renders a frame set (one page frame, one around
267 legend/scalebar and one around the map), the map, the legend and
268 the scalebar on the given DC. The map is rendered with the
269 region displayed in the canvas view, centered on the area
270 available for map display.
271 """
272
273 self.selected_layer = selected_layer
274 self.selected_shapes = selected_shapes
275
276 # Get some dimensions
277 llx, lly, urx, ury = self.region
278 mminx, mminy, mmaxx, mmaxy = self.destination_region
279
280 # Manipulate the offset to position the map
281 offx, offy = self.offset
282 # 1. Shift to corner of map drawing area
283 offx = offx + mminx
284 offy = offy + mminy
285
286 # 2. Center the map on the map drawing area:
287 # region identifies the region on the canvas view:
288 # center of map drawing area - half the size of region: rendering origin
289 self.shiftx = (mmaxx - mminx)*0.5 - (urx - llx)*0.5
290 self.shifty = (mmaxy - mminy)*0.5 - (ury - lly)*0.5
291
292 self.offset = (offx+self.shiftx, offy+self.shifty)
293 self.region = (llx + self.shiftx, lly + self.shifty, urx, ury)
294
295 # Draw the map
296 self.dc.BeginDrawing()
297 self.dc.DestroyClippingRegion()
298 self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
299 urx, ury)
300 self.render_map()
301 self.dc.EndDrawing()
302
303 # Draw the rest (frames, legend, scalebar)
304 self.dc.BeginDrawing()
305 self.dc.DestroyClippingRegion()
306
307 # Force the font for Legend drawing
308 font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
309 self.dc.SetFont(font)
310
311 self.render_frame()
312 self.render_legend()
313 self.render_scalebar()
314 self.dc.EndDrawing()
315
316 def render_frame(self):
317 """Render the frames for map and legend/scalebar."""
318
319 dc = self.dc
320 dc.SetPen(wxBLACK_PEN)
321 dc.SetBrush(wxTRANSPARENT_BRUSH)
322
323 # Dimension stuff
324 width, height = dc.GetSizeTuple()
325 mminx, mminy, mmaxx, mmaxy = self.destination_region
326
327 # Page Frame
328 dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
329
330 # Map Frame
331 llx, lly, urx, ury = self.region
332 dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
333
334 # Legend Frame
335 dc.DrawRectangle(mmaxx+10,mminy,(width-20) - (mmaxx+10), mmaxy-mminy)
336
337 dc.DestroyClippingRegion()
338 dc.SetClippingRegion(mmaxx+10,mminy,
339 (width-20) - (mmaxx+10), mmaxy-mminy)
340
341 def render_legend(self):
342 """Render the legend on the Map."""
343
344 previewer = ClassDataPreviewer()
345 dc = self.dc
346 dc.SetPen(wxBLACK_PEN)
347 dc.SetBrush(wxTRANSPARENT_BRUSH)
348
349 # Dimension stuff
350 width, height = dc.GetSizeTuple()
351 mminx, mminy, mmaxx, mmaxy = self.destination_region
352 textwidth, textheight = dc.GetTextExtent("0")
353 iconwidth = textheight
354 iconheight = textheight
355 stepy = textheight+3
356 dx = 10
357 posx = mmaxx + 10 + 5 # 10 pix distance mapframe/legend frame,
358 # 5 pix inside legend frame
359 posy = mminy + 5 # 5 pix inside legend frame
360
361 # Render the legend
362 dc.SetTextForeground(wxBLACK)
363 if self.map.HasLayers():
364 layers = self.map.Layers()[:]
365 layers.reverse()
366 for l in layers:
367 if l.Visible():
368 # Render title
369 dc.DrawText(l.Title(), posx, posy)
370 posy+=stepy
371 if l.HasClassification():
372 # Render classification
373 clazz = l.GetClassification()
374 shapeType = l.ShapeType()
375 for g in clazz:
376 if g.IsVisible():
377 previewer.Draw(dc,
378 wxRect(posx+dx, posy,
379 iconwidth, iconheight),
380 g.GetProperties(), shapeType)
381 dc.DrawText(g.GetDisplayText(),
382 posx+2*dx+iconwidth, posy)
383 posy+=stepy
384
385 def render_scalebar(self):
386 """Render the scalebar."""
387
388 scalebar = ScaleBar(self.map)
389
390 # Dimension stuff
391 width, height = self.dc.GetSizeTuple()
392 mminx, mminy, mmaxx, mmaxy = self.destination_region
393
394 # Render the scalebar
395 scalebar.DrawScaleBar(self.scale, self.dc,
396 (mmaxx+10+5, mmaxy-25),
397 ((width-15-5) - (mmaxx+10+5),20)
398 )
399 # 10 pix between map and legend frame, 5 pix inside legend frame
400 # 25 pix from the legend frame bottom line
401 # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
402 # Height: 20
403
404 class PrinterRenderer(ExportRenderer):
405
406 # Printing as well as Export / Screen display only the visible layer.
407 honor_visibility = 1
408

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26