/[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 2537 - (show annotations)
Fri Jan 21 14:01:25 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: 14399 byte(s)
Improved rendering raster layers by changing the return format of
the ProjectRasterFile function.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26