/[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 535 - (hide annotations)
Fri Mar 14 20:42:18 2003 UTC (21 years, 11 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 10336 byte(s)
Implement multiple selected shapes

* Thuban/UI/selection.py: New module with a class to represent the
selection.

* Thuban/UI/messages.py (SELECTED_TABLE, SELECTED_MAP): Remove
these unused messages

* Thuban/UI/application.py (ThubanApplication.OnInit)
(ThubanApplication.OnExit, ThubanApplication.SetSession): The
interactor is gone now.
(ThubanApplication.CreateMainWindow): There is no interactor
anymore so we pass None as the interactor argument for now for
compatibility.

* Thuban/UI/view.py (MapCanvas.delegated_messages)
(MapCanvas.Subscribe, MapCanvas.Unsubscribe): In Subscribe and
Unsubscribe, delegate messages according to the delegated_messages
class variable.
(MapCanvas.__getattr__, MapCanvas.delegated_methods): Get some
attributes from instance variables as described with the
delegated_methods class variable.
(MapCanvas.__init__): New instance variable selection holding the
current selection
(MapCanvas.do_redraw): Deal with multiple selected shapes. Simply
pass them on to the renderer
(MapCanvas.SetMap): Clear the selection when a different map is
selected.
(MapCanvas.shape_selected): Simple force a complete redraw. The
selection class now takes care of only issueing SHAPES_SELECTED
messages when the set of selected shapes actually does change.
(MapCanvas.SelectShapeAt): The selection is now managed in
self.selection

* Thuban/UI/mainwindow.py (MainWindow.delegated_messages)
(MainWindow.Subscribe, MainWindow.Unsubscribe): In Subscribe and
Unsubscribe, delegate messages according to the delegated_messages
class variable.
(MainWindow.delegated_methods, MainWindow.__getattr__): Get some
attributes from instance variables as described with the
delegated_methods class variable.
(MainWindow.__init__): The interactor as ivar is gone. The
parameter is still there for compatibility. The selection messages
now come from the canvas.
(MainWindow.current_layer, MainWindow.has_selected_layer):
Delegate to the the canvas.
(MainWindow.LayerShowTable, MainWindow.Classify)
(MainWindow.identify_view_on_demand): The dialogs don't need the
interactor parameter anymore.

* Thuban/UI/tableview.py (TableFrame.__init__)
(LayerTableFrame.__init__, LayerTableFrame.OnClose)
(LayerTableFrame.row_selected): The interactor is gone. It's job
from the dialog's point of view is now done by the mainwindow,
i.e. the parent. Subscribe to SHAPES_SELECTED instead
of SELECTED_SHAPE

* Thuban/UI/dialogs.py (NonModalDialog.__init__): The interactor
is gone. It's job from the dialog's point of view is now done by
the mainwindow, i.e. the parent.

* Thuban/UI/classifier.py (Classifier.__init__): The interactor is
gone. It's job from the dialog's point of view is now done by the
mainwindow, i.e. the parent.

* Thuban/UI/tree.py (SessionTreeView.__init__): The interactor is
gone. It's job from the dialog's point of view is now done by the
mainwindow, i.e. the parent.
(SessionTreeCtrl.__init__): New parameter mainwindow which is
stored as self.mainwindow. The mainwindow is need so that the tree
can still subscribe to the selection messages.
(SessionTreeCtrl.__init__, SessionTreeCtrl.unsubscribe_all)
(SessionTreeCtrl.update_tree, SessionTreeCtrl.OnSelChanged): The
selection is now accessible through the mainwindow. Subscribe to
SHAPES_SELECTED instead of SELECTED_SHAPE

* Thuban/UI/identifyview.py (IdentifyView.__init__): Use the
SHAPES_SELECTED message now.
(IdentifyView.selected_shape): Now subscribed to SHAPES_SELECTED,
so deal with multiple shapes
(IdentifyView.__init__, IdentifyView.OnClose): The interactor is
gone. It's job from the dialog's point of view is now done by the
mainwindow, i.e. the parent.

* Thuban/UI/renderer.py (ScreenRenderer.RenderMap): Rename the
selected_shape parameter and ivar to selected_shapes. It's now a
list of shape ids.
(MapRenderer.draw_label_layer): Deal with multiple selected
shapes. Rearrange the code a bit so that the setup and shape type
distinctions are only executed once.

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 bh 6 #
6     # This program is free software under the GPL (>=v2)
7     # Read the file COPYING coming with Thuban for details.
8    
9     __version__ = "$Revision$"
10    
11 jonathan 442 from wxPython.wx import wxPoint, wxPen, wxBrush, wxFont, \
12 bh 6 wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
13     wxBLACK, wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL
14    
15     from wxproj import draw_polygon_shape
16    
17 jan 374 from Thuban import _
18 jonathan 442 from Thuban.UI.common import *
19 jan 374
20 bh 6 from Thuban.Model.layer import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \
21     SHAPETYPE_POINT
22     from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
23     ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE
24    
25 jonathan 394 from Thuban.Model.classification import Classification
26 jonathan 416 from Thuban.Model.color import Color
27 bh 6
28 jonathan 394
29 bh 6 class MapRenderer:
30    
31     """Class to render a map onto a wxDC"""
32    
33     honor_visibility = 1
34    
35     def __init__(self, dc, scale, offset, resolution = 72.0,
36     honor_visibility = None):
37     """Inititalize the renderer.
38    
39     dc -- the wxPython DC to render on
40     scale, offset -- the scale factor and translation to convert
41     between projected coordinates and the DC coordinates
42    
43     resolution -- the assumed resolution of the DC. Used to convert
44     absolute lengths like font sizes to DC coordinates
45    
46     honor_visibility -- boolean. If true, honor the visibility flag
47     of the layers, otherwise draw all layers. If None, use
48     the renderer's default.
49     """
50     # resolution in pixel/inch
51     self.dc = dc
52     self.scale = scale
53     self.offset = offset
54     if honor_visibility is not None:
55     self.honor_visibility = honor_visibility
56     # store the resolution in pixel/point because it's more useful
57     # later.
58     self.resolution = resolution / 72.0
59    
60     def render_map(self, map):
61     self.map = map
62     for layer in map.Layers():
63     # if honor_visibility is true, only draw visible layers,
64     # otherwise draw all layers
65     if not self.honor_visibility or layer.Visible():
66     self.draw_shape_layer(layer)
67     self.draw_label_layer(map.LabelLayer())
68    
69     def draw_shape_layer(self, layer):
70     scale = self.scale
71     offx, offy = self.offset
72    
73     map_proj = self.map.projection
74     layer_proj = layer.projection
75    
76     shapetype = layer.ShapeType()
77    
78 jonathan 362 brush = wxTRANSPARENT_BRUSH
79     pen = wxTRANSPARENT_PEN
80    
81     old_prop = None
82 jonathan 468 lc = layer.GetClassification()
83     field = lc.GetField()
84    
85     defaultProps = lc.GetDefaultGroup().GetProperties()
86    
87 jonathan 362 for i in self.layer_ids(layer):
88     value = None
89    
90     if field is not None:
91 jonathan 468 try:
92     record = layer.table.read_record(i)
93     if record is not None:
94     value = record[field]
95     except:
96     pass
97 jonathan 362
98 jonathan 468 #
99     # if the above statements fail 'value' should
100     # be null, at which point this call will
101     # at least retreive the NullData
102     #
103     prop = lc.GetProperties(value)
104     else:
105     prop = defaultProps
106 jonathan 362
107 jonathan 442 # don't recreate new objects if they are the same as before
108     if prop != old_prop:
109 jonathan 362 old_prop = prop
110    
111     if shapetype == SHAPETYPE_ARC:
112 jonathan 416 fill = Color.None
113 jonathan 362 else:
114 jonathan 394 fill = prop.GetFill()
115 jonathan 362
116 jonathan 416 if fill is Color.None:
117 jonathan 362 brush = wxTRANSPARENT_BRUSH
118     else:
119 jonathan 442 color = Color2wxColour(fill)
120 jonathan 362 brush = wxBrush(color, wxSOLID)
121    
122 jonathan 468 stroke = prop.GetLineColor()
123     stroke_width = prop.GetLineWidth()
124 jonathan 416 if stroke is Color.None:
125 jonathan 362 pen = wxTRANSPARENT_PEN
126     else:
127 jonathan 442 color = Color2wxColour(stroke)
128 jonathan 362 pen = wxPen(color, stroke_width, wxSOLID)
129    
130     if shapetype == SHAPETYPE_POINT:
131     self.dc.SetBrush(brush)
132     self.dc.SetPen(pen)
133     self.draw_point_shape(layer, i)
134     else:
135 bh 6 self.draw_polygon_shape(layer, i, pen, brush)
136    
137 bh 144 def layer_ids(self, layer):
138     """Return the shape ids of the given layer that have to be drawn.
139    
140     The default implementation simply returns all ids in the layer.
141     Override in derived classes to be more precise.
142     """
143     return range(layer.NumShapes())
144    
145 bh 6 def draw_polygon_shape(self, layer, index, pen, brush):
146     offx, offy = self.offset
147     draw_polygon_shape(layer.shapefile.cobject(), index,
148     self.dc, pen, brush,
149     self.map.projection, layer.projection,
150     self.scale, -self.scale, offx, offy)
151    
152     def projected_points(self, layer, index):
153     proj = self.map.projection
154     if proj is not None:
155     forward = proj.Forward
156     else:
157     forward = None
158     proj = layer.projection
159     if proj is not None:
160     inverse = proj.Inverse
161     else:
162     inverse = None
163     shape = layer.Shape(index)
164     points = []
165     scale = self.scale
166     offx, offy = self.offset
167     for x, y in shape.Points():
168     if inverse:
169     x, y = inverse(x, y)
170     if forward:
171     x, y = forward(x, y)
172     points.append(wxPoint(x * scale + offx,
173     -y * scale + offy))
174     return points
175    
176     def draw_arc_shape(self, layer, index):
177     points = self.projected_points(layer, index)
178     self.dc.DrawLines(points)
179    
180     def draw_point_shape(self, layer, index):
181     p = self.projected_points(layer, index)[0]
182     radius = self.resolution * 5
183     self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)
184    
185     def draw_label_layer(self, layer):
186     scale = self.scale
187     offx, offy = self.offset
188    
189     font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
190     self.dc.SetFont(font)
191    
192     map_proj = self.map.projection
193     if map_proj is not None:
194     forward = map_proj.Forward
195     else:
196     forward = None
197    
198     for label in layer.Labels():
199     x = label.x
200     y = label.y
201     text = label.text
202     if forward:
203     x, y = forward(x, y)
204     x = x * scale + offx
205     y = -y * scale + offy
206     width, height = self.dc.GetTextExtent(text)
207     if label.halign == ALIGN_LEFT:
208     # nothing to be done
209     pass
210     elif label.halign == ALIGN_RIGHT:
211     x = x - width
212     elif label.halign == ALIGN_CENTER:
213     x = x - width/2
214     if label.valign == ALIGN_TOP:
215     # nothing to be done
216     pass
217     elif label.valign == ALIGN_BOTTOM:
218     y = y - height
219     elif label.valign == ALIGN_CENTER:
220     y = y - height/2
221     self.dc.DrawText(text, x, y)
222    
223    
224     class ScreenRenderer(MapRenderer):
225    
226     # On the screen we want to see only visible layers by default
227     honor_visibility = 1
228    
229 bh 535 def RenderMap(self, map, region, selected_layer, selected_shapes):
230 bh 144 """Render the map.
231    
232 bh 148 Only the given region (a tuple in window coordinates as returned
233     by a wxrect's asTuple method) needs to be redrawn. Highlight the
234 bh 535 shapes given by the ids in selected_shapes in the
235     selected_layer.
236 bh 144 """
237     self.update_region = region
238 bh 6 self.selected_layer = selected_layer
239 bh 535 self.selected_shapes = selected_shapes
240 bh 6 self.render_map(map)
241    
242     def draw_shape_layer(self, layer):
243     MapRenderer.draw_shape_layer(self, layer)
244 bh 535 if layer is self.selected_layer and self.selected_shapes:
245 bh 6 pen = wxPen(wxBLACK, 3, wxSOLID)
246     brush = wxBrush(wxBLACK, wxCROSS_HATCH)
247 bh 535
248 bh 6 shapetype = layer.ShapeType()
249     if shapetype == SHAPETYPE_POLYGON:
250 bh 535 func = self.draw_polygon_shape
251     args = (pen, brush)
252 bh 290 elif shapetype == SHAPETYPE_ARC:
253 bh 535 func = self.draw_polygon_shape
254     args = (pen, None)
255     elif shapetype == SHAPETYPE_POINT:
256 bh 6 self.dc.SetBrush(brush)
257     self.dc.SetPen(pen)
258 bh 535 func = self.draw_point_shape
259     args = ()
260     else:
261     raise TypeError(_("Unhandled shape type %s") % shapetype)
262 bh 6
263 bh 535 for index in self.selected_shapes:
264     func(layer, index, *args)
265    
266 bh 144 def layer_ids(self, layer):
267     """Return the shapeids covered by the region that has to be redrawn
268 bh 6
269 bh 144 Call the layer's ShapesInRegion method to determine the ids so
270     that it can use the quadtree.
271     """
272     # FIXME: the quad-tree should be built from the projected
273     # coordinates not the lat-long ones because it's not trivial to
274     # determine an appropriate rectangle in lat-long for a given
275     # rectangle in projected coordinates which we have to start from
276     # here.
277     proj = self.map.projection
278     if proj is not None:
279     inverse = proj.Inverse
280     else:
281     inverse = None
282    
283     scale = self.scale
284     offx, offy = self.offset
285     xs = []
286     ys = []
287 bh 148 x, y, width, height = self.update_region
288 bh 144 for winx, winy in ((x, y), (x + width, y),
289     (x + width, y + height), (x, y + height)):
290     px = (winx - offx) / scale
291     py = -(winy - offy) / scale
292     if inverse:
293     px, py = inverse(px, py)
294     xs.append(px)
295     ys.append(py)
296     left = min(xs)
297     right = max(xs)
298     top = max(ys)
299     bottom = min(ys)
300    
301     return layer.ShapesInRegion((left, bottom, right, top))
302    
303    
304 bh 6 class PrinterRender(MapRenderer):
305    
306     # When printing we want to see all layers
307     honor_visibility = 0
308    
309     RenderMap = MapRenderer.render_map
310    

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26