/[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 1219 - (show annotations)
Mon Jun 16 17:42:54 2003 UTC (21 years, 8 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 18997 byte(s)
Update to the layer interface: Direct access to the table,
shapetable, shapefile and filename attributes is now actively
deprecated by issuing deprecation warnings for all places where
this happens.

* Thuban/Model/layer.py (Layer.__getattr__): New. Implement access
to the instance variables table, shapetable, shapefile and
filename via __getattr__ so that we can issue a deprecation
warning.
(Layer.SetShapeStore): Don't set the deprecated instance variables
any more
(Layer.SetShapeStore): Don't use deprecated layer instance
variables
(Layer.Destroy): No need to explicitly remove the instance
variables any more
(Layer.GetFieldType, Layer.Shape): Don't use deprecated layer
instance variables

* Thuban/UI/classgen.py (ClassGenDialog.__init__)
(GenUniformPanel._OnRetrieve, GenUniquePanel._OnRetrieve)
(GenQuantilesPanel.GetList, GenQuantilesPanel.OnRetrieve): Don't
use deprecated layer instance variables

* Thuban/UI/classifier.py (Classifier.__init__): Don't use
deprecated layer instance variables

* Thuban/UI/identifyview.py (IdentifyListCtrl.selected_shape)
(IdentifyGridCtrl.selected_shape): Don't set the deprecated layer
instance variables

* Thuban/UI/tableview.py (LayerTableGrid.select_shapes): Don't use
deprecated layer instance variables

* Thuban/UI/mainwindow.py (MainWindow.LayerShowTable): Don't use
deprecated layer instance variables

* Thuban/Model/save.py (SessionSaver.write_layer): Don't use
deprecated layer instance variables

* Thuban/UI/renderer.py (MapRenderer.draw_shape_layer)
(MapRenderer.polygon_render_param): Don't use deprecated layer instance
variables

* test/runtests.py (main): Turn Thuban's deprecation warnings into
errors so that they're cought by the tests

* test/test_load.py (TestSingleLayer.test): Don't use deprecated
layer instance variables

1 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Bernhard Herzog <[email protected]>
4 # Jonathan Coles <[email protected]>
5 # Frank Koormann <[email protected]>
6 #
7 # This program is free software under the GPL (>=v2)
8 # Read the file COPYING coming with Thuban for details.
9
10 __version__ = "$Revision$"
11
12 import cStringIO
13
14 from Thuban import _
15
16 from wxPython.wx import wxMemoryDC, wxEmptyBitmap, \
17 wxPoint, wxRect, wxPen, wxBrush, wxFont, \
18 wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
19 wxBLACK_PEN, wxRED_PEN, wxBLACK, \
20 wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
21 wxBitmap, wxImageFromBitmap, wxBitmapFromImage, \
22 wxImageFromStream, wxBITMAP_TYPE_BMP
23
24 from wxproj import draw_polygon_shape, draw_polygon_init
25 from gdalwarp import ProjectRasterFile
26
27 from Thuban.UI.common import Color2wxColour
28 from Thuban.UI.classifier import ClassDataPreviewer
29 from Thuban.UI.scalebar import ScaleBar
30
31 from Thuban.Model.layer import Layer, RasterLayer, \
32 SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
33 from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
34 ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE
35
36 from Thuban.Model.classification import Classification
37 from Thuban.Model.color import Color
38 import Thuban.Model.resource
39
40 class MapRenderer:
41
42 """Class to render a map onto a wxDC"""
43
44 honor_visibility = 1
45
46 def __init__(self, dc, scale, offset, resolution = 72.0,
47 honor_visibility = None):
48 """Inititalize the renderer.
49
50 dc -- the wxPython DC to render on
51 scale, offset -- the scale factor and translation to convert
52 between projected coordinates and the DC coordinates
53
54 resolution -- the assumed resolution of the DC. Used to convert
55 absolute lengths like font sizes to DC coordinates
56
57 honor_visibility -- boolean. If true, honor the visibility flag
58 of the layers, otherwise draw all layers. If None, use
59 the renderer's default.
60 """
61 # resolution in pixel/inch
62
63 self.dc = dc
64 self.scale = scale
65 self.offset = offset
66 if honor_visibility is not None:
67 self.honor_visibility = honor_visibility
68 # store the resolution in pixel/point because it's more useful
69 # later.
70 self.resolution = resolution / 72.0
71
72 def render_map(self, map):
73 self.map = map
74 seenRaster = True
75
76 if self.scale == 0:
77 return
78
79 #
80 # This is only a good optimization if there is only one
81 # raster layer and the image covers the entire window (as
82 # it currently does). We note if there is a raster layer
83 # and only begin drawing layers once we have drawn it.
84 # That way we avoid drawing layers that won't be seen.
85 #
86 for layer in map.Layers():
87 if isinstance(layer, RasterLayer) and layer.Visible():
88 seenRaster = False
89 break
90
91 for layer in map.Layers():
92 # if honor_visibility is true, only draw visible layers,
93 # otherwise draw all layers
94 if not self.honor_visibility or layer.Visible():
95 if isinstance(layer, Layer) and seenRaster:
96 self.draw_shape_layer(layer)
97 elif isinstance(layer, RasterLayer) \
98 and Thuban.Model.resource.has_gdal_support():
99 self.draw_raster_layer(layer)
100 seenRaster = True
101
102 self.draw_label_layer(map.LabelLayer())
103
104 def draw_shape_layer(self, layer):
105 scale = self.scale
106 offx, offy = self.offset
107
108 map_proj = self.map.projection
109 layer_proj = layer.projection
110
111 shapetype = layer.ShapeType()
112
113 brush = wxTRANSPARENT_BRUSH
114 pen = wxTRANSPARENT_PEN
115
116 old_prop = None
117 old_group = None
118 lc = layer.GetClassification()
119 field = lc.GetField()
120 defaultGroup = lc.GetDefaultGroup()
121
122
123 if shapetype != SHAPETYPE_POINT:
124 polygon_render_param = self.polygon_render_param(layer)
125
126 if shapetype == SHAPETYPE_POINT:
127 draw_func = lambda i: \
128 self.draw_point_shape(layer, i)
129 else:
130 draw_func = lambda i: \
131 self.draw_polygon_shape(polygon_render_param, i, pen, brush)
132
133 table = layer.ShapeStore().Table()
134 for i in self.layer_ids(layer):
135
136 if field is None:
137 group = defaultGroup
138 else:
139 record = table.ReadRowAsDict(i)
140 assert record is not None
141 group = lc.FindGroup(record[field])
142
143
144 if not group.IsVisible():
145 continue
146
147
148 # don't recreate new objects if they are the same as before
149 if group is not old_group:
150 old_group = group
151
152 prop = group.GetProperties()
153
154 if prop != old_prop:
155 old_prop = prop
156
157 if shapetype == SHAPETYPE_ARC:
158 fill = Color.Transparent
159 else:
160 fill = prop.GetFill()
161
162
163 if fill is Color.Transparent:
164 brush = wxTRANSPARENT_BRUSH
165 else:
166 color = Color2wxColour(fill)
167 brush = wxBrush(color, wxSOLID)
168
169 stroke = prop.GetLineColor()
170 stroke_width = prop.GetLineWidth()
171 if stroke is Color.Transparent:
172 pen = wxTRANSPARENT_PEN
173 else:
174 color = Color2wxColour(stroke)
175 pen = wxPen(color, stroke_width, wxSOLID)
176
177 if shapetype == SHAPETYPE_POINT:
178 self.dc.SetBrush(brush)
179 self.dc.SetPen(pen)
180
181 draw_func(i)
182
183 def draw_raster_layer(self, layer):
184 data = None
185 offx, offy = self.offset
186 width, height = self.dc.GetSizeTuple()
187
188 inProj = ""
189 proj = layer.GetProjection()
190 if proj is not None:
191 for p in proj.GetAllParameters():
192 inProj += "+" + p + " "
193
194 outProj = ""
195 proj = self.map.GetProjection()
196 if proj is not None:
197 for p in proj.GetAllParameters():
198 outProj += "+" + p + " "
199
200 xmin = (0 - offx) / self.scale
201 ymin = (offy - height) / self.scale
202 xmax = (width - offx) / self.scale
203 ymax = (offy - 0) / self.scale
204
205 try:
206 data = ProjectRasterFile(
207 layer.GetImageFilename(),
208 inProj,
209 outProj,
210 (xmin, ymin, xmax, ymax),
211 "", (width, height))
212 except IOError, (strerr):
213 print strerr
214 except (AttributeError, ValueError):
215 pass
216 else:
217 if data is not None:
218 stream = cStringIO.StringIO(data)
219 image = wxImageFromStream(stream, wxBITMAP_TYPE_BMP)
220 bitmap = wxBitmapFromImage(image)
221 self.dc.BeginDrawing()
222 self.dc.DrawBitmap(bitmap, 0, 0)
223 self.dc.EndDrawing()
224
225 def layer_ids(self, layer):
226 """Return the shape ids of the given layer that have to be drawn.
227
228 The default implementation simply returns all ids in the layer.
229 Override in derived classes to be more precise.
230 """
231 return range(layer.NumShapes())
232
233 def polygon_render_param(self, layer):
234 """Return the low-lever render parameter for the layer"""
235 offx, offy = self.offset
236 return draw_polygon_init(layer.ShapeStore().Shapefile(), self.dc,
237 self.map.projection,
238 layer.projection,
239 self.scale, -self.scale,
240 offx, offy)
241
242 def draw_polygon_shape(self, draw_polygon_info, index, pen, brush):
243 draw_polygon_shape(draw_polygon_info, index, pen, brush)
244
245 def projected_points(self, layer, index):
246 proj = self.map.GetProjection()
247 if proj is not None:
248 forward = proj.Forward
249 else:
250 forward = None
251 proj = layer.GetProjection()
252 if proj is not None:
253 inverse = proj.Inverse
254 else:
255 inverse = None
256 shape = layer.Shape(index)
257 points = []
258 scale = self.scale
259 offx, offy = self.offset
260 for x, y in shape.Points():
261 if inverse:
262 x, y = inverse(x, y)
263 if forward:
264 x, y = forward(x, y)
265 points.append(wxPoint(x * scale + offx,
266 -y * scale + offy))
267 return points
268
269 def draw_arc_shape(self, layer, index):
270 points = self.projected_points(layer, index)
271 self.dc.DrawLines(points)
272
273 def draw_point_shape(self, layer, index):
274 pp = self.projected_points(layer, index)
275
276 if len(pp) == 0: return # ignore Null Shapes which have no points
277
278 p = pp[0]
279 radius = self.resolution * 5
280 self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)
281
282 def draw_label_layer(self, layer):
283 scale = self.scale
284 offx, offy = self.offset
285
286 font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
287 self.dc.SetFont(font)
288
289 map_proj = self.map.projection
290 if map_proj is not None:
291 forward = map_proj.Forward
292 else:
293 forward = None
294
295 for label in layer.Labels():
296 x = label.x
297 y = label.y
298 text = label.text
299 if forward:
300 x, y = forward(x, y)
301 x = x * scale + offx
302 y = -y * scale + offy
303 width, height = self.dc.GetTextExtent(text)
304 if label.halign == ALIGN_LEFT:
305 # nothing to be done
306 pass
307 elif label.halign == ALIGN_RIGHT:
308 x = x - width
309 elif label.halign == ALIGN_CENTER:
310 x = x - width/2
311 if label.valign == ALIGN_TOP:
312 # nothing to be done
313 pass
314 elif label.valign == ALIGN_BOTTOM:
315 y = y - height
316 elif label.valign == ALIGN_CENTER:
317 y = y - height/2
318 self.dc.DrawText(text, x, y)
319
320
321 class ScreenRenderer(MapRenderer):
322
323 # On the screen we want to see only visible layers by default
324 honor_visibility = 1
325
326 def RenderMap(self, map, region, selected_layer, selected_shapes):
327 """Render the map.
328
329 Only the given region (a tuple in window coordinates as returned
330 by a wxrect's asTuple method) needs to be redrawn. Highlight the
331 shapes given by the ids in selected_shapes in the
332 selected_layer.
333 """
334 self.update_region = region
335 self.selected_layer = selected_layer
336 self.selected_shapes = selected_shapes
337 self.render_map(map)
338
339 def draw_shape_layer(self, layer):
340 MapRenderer.draw_shape_layer(self, layer)
341 if layer is self.selected_layer and self.selected_shapes:
342 pen = wxPen(wxBLACK, 3, wxSOLID)
343 brush = wxBrush(wxBLACK, wxCROSS_HATCH)
344
345 shapetype = layer.ShapeType()
346 if shapetype == SHAPETYPE_POLYGON:
347 offx, offy = self.offset
348 renderparam = self.polygon_render_param(layer)
349 func = self.draw_polygon_shape
350 args = (pen, brush)
351 elif shapetype == SHAPETYPE_ARC:
352 renderparam = self.polygon_render_param(layer)
353 func = self.draw_polygon_shape
354 args = (pen, None)
355 elif shapetype == SHAPETYPE_POINT:
356 renderparam = layer
357 self.dc.SetBrush(brush)
358 self.dc.SetPen(pen)
359 func = self.draw_point_shape
360 args = ()
361 else:
362 raise TypeError(_("Unhandled shape type %s") % shapetype)
363
364 for index in self.selected_shapes:
365 func(renderparam, index, *args)
366
367
368 def layer_ids(self, layer):
369 """Return the shapeids covered by the region that has to be redrawn
370
371 Call the layer's ShapesInRegion method to determine the ids so
372 that it can use the quadtree.
373 """
374 # FIXME: the quad-tree should be built from the projected
375 # coordinates not the lat-long ones because it's not trivial to
376 # determine an appropriate rectangle in lat-long for a given
377 # rectangle in projected coordinates which we have to start from
378 # here.
379 proj = self.map.projection
380 if proj is not None:
381 inverse = proj.Inverse
382 else:
383 inverse = None
384
385 scale = self.scale
386 offx, offy = self.offset
387 xs = []
388 ys = []
389 x, y, width, height = self.update_region
390 for winx, winy in ((x, y), (x + width, y),
391 (x + width, y + height), (x, y + height)):
392 px = (winx - offx) / scale
393 py = -(winy - offy) / scale
394 if inverse:
395 px, py = inverse(px, py)
396 xs.append(px)
397 ys.append(py)
398 left = min(xs)
399 right = max(xs)
400 top = max(ys)
401 bottom = min(ys)
402
403 return layer.ShapesInRegion((left, bottom, right, top))
404
405
406 class ExportRenderer(ScreenRenderer):
407
408 honor_visibility = 1
409
410 def RenderMap(self, map, region, mapregion,
411 selected_layer, selected_shapes ):
412 """Render the map.
413
414 The rendering device has been specified during initialisation.
415 The device border distance was set in Thuban.UI.view.OutputTranform().
416
417 RenderMap renders a frame set (one page frame, one around
418 legend/scalebar and one around the map), the map, the legend and the
419 scalebar on the given DC. The map is rendered with the region displayed
420 in the canvas view, centered on the area available for map display.
421 """
422
423 self.update_region = region
424 self.selected_layer = selected_layer
425 self.selected_shapes = selected_shapes
426
427 # Get some dimensions
428 llx, lly, urx, ury = region
429 self.mapregion = mapregion
430 mminx, mminy, mmaxx, mmaxy = self.mapregion
431
432 # Manipulate the offset to position the map
433 offx, offy = self.offset
434 # 1. Shift to corner of map drawing area
435 offx = offx + mminx
436 offy = offy + mminy
437
438 # 2. Center the map on the map drawing area:
439 # region identifies the region on the canvas view:
440 # center of map drawing area - half the size of region: rendering origin
441 self.shiftx = (mmaxx - mminx)*0.5 - (urx - llx)*0.5
442 self.shifty = (mmaxy - mminy)*0.5 - (ury - lly)*0.5
443
444 self.offset = (offx+self.shiftx, offy+self.shifty)
445
446 # Draw the map
447 self.dc.BeginDrawing()
448 self.dc.DestroyClippingRegion()
449 self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
450 urx, ury)
451 self.render_map(map)
452 self.dc.EndDrawing()
453
454 # Draw the rest (frames, legend, scalebar)
455 self.dc.BeginDrawing()
456 self.dc.DestroyClippingRegion()
457
458 # Force the font for Legend drawing
459 font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
460 self.dc.SetFont(font)
461
462 self.render_frame(region)
463 self.render_legend(map)
464 self.render_scalebar(map)
465 self.dc.EndDrawing()
466
467 def render_frame(self, region):
468 """Render the frames for map and legend/scalebar."""
469
470 dc = self.dc
471 dc.SetPen(wxBLACK_PEN)
472 dc.SetBrush(wxTRANSPARENT_BRUSH)
473
474 # Dimension stuff
475 width, height = dc.GetSizeTuple()
476 mminx, mminy, mmaxx, mmaxy = self.mapregion
477
478 # Page Frame
479 dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
480
481 # Map Frame
482 llx, lly, urx, ury = region
483 dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
484
485 # Legend Frame
486 dc.DrawRectangle(mmaxx+10,mminy,(width-20) - (mmaxx+10), mmaxy-mminy)
487
488 dc.DestroyClippingRegion()
489 dc.SetClippingRegion(mmaxx+10,mminy,
490 (width-20) - (mmaxx+10), mmaxy-mminy)
491
492 def render_legend(self, map):
493 """Render the legend on the Map."""
494
495 previewer = ClassDataPreviewer()
496 dc = self.dc
497 dc.SetPen(wxBLACK_PEN)
498 dc.SetBrush(wxTRANSPARENT_BRUSH)
499
500 # Dimension stuff
501 width, height = dc.GetSizeTuple()
502 mminx, mminy, mmaxx, mmaxy = self.mapregion
503 textwidth, textheight = dc.GetTextExtent("0")
504 iconwidth = textheight
505 iconheight = textheight
506 stepy = textheight+3
507 dx = 10
508 posx = mmaxx + 10 + 5 # 10 pix distance mapframe/legend frame,
509 # 5 pix inside legend frame
510 posy = mminy + 5 # 5 pix inside legend frame
511
512 # Render the legend
513 dc.SetTextForeground(wxBLACK)
514 if map.HasLayers():
515 for l in map.Layers():
516 if l.Visible():
517 # Render title
518 dc.DrawText(l.Title(), posx, posy)
519 posy+=stepy
520 # Render classification
521 clazz = l.GetClassification()
522 shapeType = l.ShapeType()
523 for g in clazz:
524 if g.IsVisible():
525 previewer.Draw(dc,
526 wxRect(posx+dx, posy, iconwidth, iconheight),
527 g.GetProperties(), shapeType)
528 dc.DrawText(g.GetDisplayText(),
529 posx+2*dx+iconwidth, posy)
530 posy+=stepy
531
532 def render_scalebar(self, map):
533 """Render the scalebar."""
534
535 scalebar = ScaleBar(map)
536
537 # Dimension stuff
538 width, height = self.dc.GetSizeTuple()
539 mminx, mminy, mmaxx, mmaxy = self.mapregion
540
541 # Render the scalebar
542 scalebar.DrawScaleBar(self.scale, self.dc,
543 (mmaxx+10+5, mmaxy-25),
544 ((width-15-5) - (mmaxx+10+5),20)
545 )
546 # 10 pix between map and legend frame, 5 pix inside legend frame
547 # 25 pix from the legend frame bottom line
548 # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
549 # Height: 20
550
551 class PrinterRenderer(ExportRenderer):
552
553 # Printing as well as Export / Screen display only the visible layer.
554 honor_visibility = 1
555

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26