/[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 535 - (show 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 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Bernhard Herzog <[email protected]>
4 # Jonathan Coles <[email protected]>
5 #
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 from wxPython.wx import wxPoint, wxPen, wxBrush, wxFont, \
12 wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
13 wxBLACK, wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL
14
15 from wxproj import draw_polygon_shape
16
17 from Thuban import _
18 from Thuban.UI.common import *
19
20 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 from Thuban.Model.classification import Classification
26 from Thuban.Model.color import Color
27
28
29 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 brush = wxTRANSPARENT_BRUSH
79 pen = wxTRANSPARENT_PEN
80
81 old_prop = None
82 lc = layer.GetClassification()
83 field = lc.GetField()
84
85 defaultProps = lc.GetDefaultGroup().GetProperties()
86
87 for i in self.layer_ids(layer):
88 value = None
89
90 if field is not None:
91 try:
92 record = layer.table.read_record(i)
93 if record is not None:
94 value = record[field]
95 except:
96 pass
97
98 #
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
107 # don't recreate new objects if they are the same as before
108 if prop != old_prop:
109 old_prop = prop
110
111 if shapetype == SHAPETYPE_ARC:
112 fill = Color.None
113 else:
114 fill = prop.GetFill()
115
116 if fill is Color.None:
117 brush = wxTRANSPARENT_BRUSH
118 else:
119 color = Color2wxColour(fill)
120 brush = wxBrush(color, wxSOLID)
121
122 stroke = prop.GetLineColor()
123 stroke_width = prop.GetLineWidth()
124 if stroke is Color.None:
125 pen = wxTRANSPARENT_PEN
126 else:
127 color = Color2wxColour(stroke)
128 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 self.draw_polygon_shape(layer, i, pen, brush)
136
137 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 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 def RenderMap(self, map, region, selected_layer, selected_shapes):
230 """Render the map.
231
232 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 shapes given by the ids in selected_shapes in the
235 selected_layer.
236 """
237 self.update_region = region
238 self.selected_layer = selected_layer
239 self.selected_shapes = selected_shapes
240 self.render_map(map)
241
242 def draw_shape_layer(self, layer):
243 MapRenderer.draw_shape_layer(self, layer)
244 if layer is self.selected_layer and self.selected_shapes:
245 pen = wxPen(wxBLACK, 3, wxSOLID)
246 brush = wxBrush(wxBLACK, wxCROSS_HATCH)
247
248 shapetype = layer.ShapeType()
249 if shapetype == SHAPETYPE_POLYGON:
250 func = self.draw_polygon_shape
251 args = (pen, brush)
252 elif shapetype == SHAPETYPE_ARC:
253 func = self.draw_polygon_shape
254 args = (pen, None)
255 elif shapetype == SHAPETYPE_POINT:
256 self.dc.SetBrush(brush)
257 self.dc.SetPen(pen)
258 func = self.draw_point_shape
259 args = ()
260 else:
261 raise TypeError(_("Unhandled shape type %s") % shapetype)
262
263 for index in self.selected_shapes:
264 func(layer, index, *args)
265
266 def layer_ids(self, layer):
267 """Return the shapeids covered by the region that has to be redrawn
268
269 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 x, y, width, height = self.update_region
288 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 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