/[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 1219 - (hide 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 bh 424 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 bh 6 # Authors:
3     # Bernhard Herzog <[email protected]>
4 jonathan 416 # Jonathan Coles <[email protected]>
5 frank 909 # Frank Koormann <[email protected]>
6 bh 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 jonathan 938 import cStringIO
13    
14 jonathan 882 from Thuban import _
15    
16 frank 909 from wxPython.wx import wxMemoryDC, wxEmptyBitmap, \
17     wxPoint, wxRect, wxPen, wxBrush, wxFont, \
18     wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
19 jonathan 938 wxBLACK_PEN, wxRED_PEN, wxBLACK, \
20     wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
21     wxBitmap, wxImageFromBitmap, wxBitmapFromImage, \
22     wxImageFromStream, wxBITMAP_TYPE_BMP
23 bh 6
24 jonathan 676 from wxproj import draw_polygon_shape, draw_polygon_init
25 jonathan 938 from gdalwarp import ProjectRasterFile
26 bh 6
27 jonathan 882 from Thuban.UI.common import Color2wxColour
28 frank 909 from Thuban.UI.classifier import ClassDataPreviewer
29     from Thuban.UI.scalebar import ScaleBar
30 jan 374
31 jonathan 938 from Thuban.Model.layer import Layer, RasterLayer, \
32     SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
33 bh 6 from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
34     ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE
35    
36 jonathan 394 from Thuban.Model.classification import Classification
37 jonathan 416 from Thuban.Model.color import Color
38 jonathan 1165 import Thuban.Model.resource
39 bh 6
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 jonathan 574
63 bh 6 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 jonathan 938 seenRaster = True
75    
76 jonathan 965 if self.scale == 0:
77     return
78    
79 jonathan 938 #
80     # This is only a good optimization if there is only one
81     # raster layer and the image covers the entire window (as
82 jonathan 1165 # 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 jonathan 938 #
86 bh 6 for layer in map.Layers():
87 jonathan 1165 if isinstance(layer, RasterLayer) and layer.Visible():
88 jonathan 938 seenRaster = False
89     break
90    
91     for layer in map.Layers():
92 bh 6 # if honor_visibility is true, only draw visible layers,
93     # otherwise draw all layers
94     if not self.honor_visibility or layer.Visible():
95 jonathan 938 if isinstance(layer, Layer) and seenRaster:
96     self.draw_shape_layer(layer)
97 jonathan 1165 elif isinstance(layer, RasterLayer) \
98     and Thuban.Model.resource.has_gdal_support():
99 jonathan 938 self.draw_raster_layer(layer)
100     seenRaster = True
101    
102 bh 6 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 jonathan 362 brush = wxTRANSPARENT_BRUSH
114     pen = wxTRANSPARENT_PEN
115    
116     old_prop = None
117 jonathan 798 old_group = None
118 jonathan 468 lc = layer.GetClassification()
119     field = lc.GetField()
120 jonathan 798 defaultGroup = lc.GetDefaultGroup()
121 jonathan 468
122 jonathan 676
123     if shapetype != SHAPETYPE_POINT:
124 bh 686 polygon_render_param = self.polygon_render_param(layer)
125 jonathan 676
126 jonathan 798 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 bh 1219
133     table = layer.ShapeStore().Table()
134 jonathan 362 for i in self.layer_ids(layer):
135    
136 jonathan 798 if field is None:
137     group = defaultGroup
138 jonathan 468 else:
139 bh 1219 record = table.ReadRowAsDict(i)
140 jonathan 798 assert record is not None
141     group = lc.FindGroup(record[field])
142 jonathan 362
143 jonathan 642
144     if not group.IsVisible():
145     continue
146    
147 jonathan 676
148 jonathan 442 # don't recreate new objects if they are the same as before
149 jonathan 798 if group is not old_group:
150     old_group = group
151 jonathan 362
152 jonathan 798 prop = group.GetProperties()
153 jonathan 676
154 jonathan 798 if prop != old_prop:
155     old_prop = prop
156 bh 686
157 jonathan 798 if shapetype == SHAPETYPE_ARC:
158     fill = Color.Transparent
159     else:
160     fill = prop.GetFill()
161 bh 686
162 jonathan 676
163 jonathan 798 if fill is Color.Transparent:
164     brush = wxTRANSPARENT_BRUSH
165     else:
166     color = Color2wxColour(fill)
167     brush = wxBrush(color, wxSOLID)
168 bh 686
169 jonathan 798 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 bh 6
177 jonathan 798 if shapetype == SHAPETYPE_POINT:
178     self.dc.SetBrush(brush)
179     self.dc.SetPen(pen)
180    
181     draw_func(i)
182    
183 jonathan 938 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 jonathan 1103 (xmin, ymin, xmax, ymax),
211     "", (width, height))
212 jonathan 965 except IOError, (strerr):
213     print strerr
214     except (AttributeError, ValueError):
215 jonathan 938 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 bh 144 def layer_ids(self, layer):
226     """Return the shape ids of the given layer that have to be drawn.
227 bh 686
228 bh 144 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 bh 686 def polygon_render_param(self, layer):
234     """Return the low-lever render parameter for the layer"""
235     offx, offy = self.offset
236 bh 1219 return draw_polygon_init(layer.ShapeStore().Shapefile(), self.dc,
237 bh 686 self.map.projection,
238     layer.projection,
239     self.scale, -self.scale,
240     offx, offy)
241 bh 6
242 bh 686 def draw_polygon_shape(self, draw_polygon_info, index, pen, brush):
243     draw_polygon_shape(draw_polygon_info, index, pen, brush)
244    
245 bh 6 def projected_points(self, layer, index):
246 jonathan 798 proj = self.map.GetProjection()
247 bh 6 if proj is not None:
248     forward = proj.Forward
249     else:
250     forward = None
251 jonathan 798 proj = layer.GetProjection()
252 bh 6 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 jonathan 588 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 bh 6 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 bh 535 def RenderMap(self, map, region, selected_layer, selected_shapes):
327 bh 144 """Render the map.
328    
329 bh 148 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 bh 535 shapes given by the ids in selected_shapes in the
332     selected_layer.
333 bh 144 """
334     self.update_region = region
335 bh 6 self.selected_layer = selected_layer
336 bh 535 self.selected_shapes = selected_shapes
337 bh 6 self.render_map(map)
338    
339     def draw_shape_layer(self, layer):
340     MapRenderer.draw_shape_layer(self, layer)
341 bh 535 if layer is self.selected_layer and self.selected_shapes:
342 bh 6 pen = wxPen(wxBLACK, 3, wxSOLID)
343     brush = wxBrush(wxBLACK, wxCROSS_HATCH)
344 bh 535
345 bh 6 shapetype = layer.ShapeType()
346     if shapetype == SHAPETYPE_POLYGON:
347 bh 686 offx, offy = self.offset
348     renderparam = self.polygon_render_param(layer)
349 bh 535 func = self.draw_polygon_shape
350     args = (pen, brush)
351 bh 290 elif shapetype == SHAPETYPE_ARC:
352 bh 686 renderparam = self.polygon_render_param(layer)
353 bh 535 func = self.draw_polygon_shape
354     args = (pen, None)
355     elif shapetype == SHAPETYPE_POINT:
356 bh 686 renderparam = layer
357 bh 6 self.dc.SetBrush(brush)
358     self.dc.SetPen(pen)
359 bh 535 func = self.draw_point_shape
360     args = ()
361     else:
362     raise TypeError(_("Unhandled shape type %s") % shapetype)
363 bh 6
364 bh 535 for index in self.selected_shapes:
365 bh 686 func(renderparam, index, *args)
366 bh 535
367 bh 686
368 bh 144 def layer_ids(self, layer):
369     """Return the shapeids covered by the region that has to be redrawn
370 bh 6
371 bh 144 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 bh 148 x, y, width, height = self.update_region
390 bh 144 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 frank 909 class ExportRenderer(ScreenRenderer):
407 bh 6
408 frank 909 honor_visibility = 1
409    
410     def RenderMap(self, map, region, mapregion,
411     selected_layer, selected_shapes ):
412     """Render the map.
413 bh 6
414 frank 909 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