/[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 798 - (show annotations)
Wed Apr 30 17:02:04 2003 UTC (21 years, 10 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 11096 byte(s)
(MapRenderer.draw_shape_layer): Optimize
        the rendering loop by reducing the number of if's, removing the
        unnecessary try/except block, and checking if the old group
        is the same as the new one (which happens a lot if there is
        no classification, or lots of shapes are in the same group).

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, draw_polygon_init
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 class MapRenderer:
29
30 """Class to render a map onto a wxDC"""
31
32 honor_visibility = 1
33
34 def __init__(self, dc, scale, offset, resolution = 72.0,
35 honor_visibility = None):
36 """Inititalize the renderer.
37
38 dc -- the wxPython DC to render on
39 scale, offset -- the scale factor and translation to convert
40 between projected coordinates and the DC coordinates
41
42 resolution -- the assumed resolution of the DC. Used to convert
43 absolute lengths like font sizes to DC coordinates
44
45 honor_visibility -- boolean. If true, honor the visibility flag
46 of the layers, otherwise draw all layers. If None, use
47 the renderer's default.
48 """
49 # resolution in pixel/inch
50
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 old_group = None
83 lc = layer.GetClassification()
84 field = lc.GetField()
85 defaultGroup = lc.GetDefaultGroup()
86
87
88 if shapetype != SHAPETYPE_POINT:
89 polygon_render_param = self.polygon_render_param(layer)
90
91 if shapetype == SHAPETYPE_POINT:
92 draw_func = lambda i: \
93 self.draw_point_shape(layer, i)
94 else:
95 draw_func = lambda i: \
96 self.draw_polygon_shape(polygon_render_param, i, pen, brush)
97
98 for i in self.layer_ids(layer):
99
100 if field is None:
101 group = defaultGroup
102 else:
103 record = layer.table.read_record(i)
104 assert record is not None
105 group = lc.FindGroup(record[field])
106
107
108 if not group.IsVisible():
109 continue
110
111
112 # don't recreate new objects if they are the same as before
113 if group is not old_group:
114 old_group = group
115
116 prop = group.GetProperties()
117
118 if prop != old_prop:
119 old_prop = prop
120
121 if shapetype == SHAPETYPE_ARC:
122 fill = Color.Transparent
123 else:
124 fill = prop.GetFill()
125
126
127 if fill is Color.Transparent:
128 brush = wxTRANSPARENT_BRUSH
129 else:
130 color = Color2wxColour(fill)
131 brush = wxBrush(color, wxSOLID)
132
133 stroke = prop.GetLineColor()
134 stroke_width = prop.GetLineWidth()
135 if stroke is Color.Transparent:
136 pen = wxTRANSPARENT_PEN
137 else:
138 color = Color2wxColour(stroke)
139 pen = wxPen(color, stroke_width, wxSOLID)
140
141 if shapetype == SHAPETYPE_POINT:
142 self.dc.SetBrush(brush)
143 self.dc.SetPen(pen)
144
145 draw_func(i)
146
147 def layer_ids(self, layer):
148 """Return the shape ids of the given layer that have to be drawn.
149
150 The default implementation simply returns all ids in the layer.
151 Override in derived classes to be more precise.
152 """
153 return range(layer.NumShapes())
154
155 def polygon_render_param(self, layer):
156 """Return the low-lever render parameter for the layer"""
157 offx, offy = self.offset
158 return draw_polygon_init(layer.shapefile, self.dc,
159 self.map.projection,
160 layer.projection,
161 self.scale, -self.scale,
162 offx, offy)
163
164 def draw_polygon_shape(self, draw_polygon_info, index, pen, brush):
165 draw_polygon_shape(draw_polygon_info, index, pen, brush)
166
167 def projected_points(self, layer, index):
168 proj = self.map.GetProjection()
169 if proj is not None:
170 forward = proj.Forward
171 else:
172 forward = None
173 proj = layer.GetProjection()
174 if proj is not None:
175 inverse = proj.Inverse
176 else:
177 inverse = None
178 shape = layer.Shape(index)
179 points = []
180 scale = self.scale
181 offx, offy = self.offset
182 for x, y in shape.Points():
183 if inverse:
184 x, y = inverse(x, y)
185 if forward:
186 x, y = forward(x, y)
187 points.append(wxPoint(x * scale + offx,
188 -y * scale + offy))
189 return points
190
191 def draw_arc_shape(self, layer, index):
192 points = self.projected_points(layer, index)
193 self.dc.DrawLines(points)
194
195 def draw_point_shape(self, layer, index):
196 pp = self.projected_points(layer, index)
197
198 if len(pp) == 0: return # ignore Null Shapes which have no points
199
200 p = pp[0]
201 radius = self.resolution * 5
202 self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)
203
204 def draw_label_layer(self, layer):
205 scale = self.scale
206 offx, offy = self.offset
207
208 font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
209 self.dc.SetFont(font)
210
211 map_proj = self.map.projection
212 if map_proj is not None:
213 forward = map_proj.Forward
214 else:
215 forward = None
216
217 for label in layer.Labels():
218 x = label.x
219 y = label.y
220 text = label.text
221 if forward:
222 x, y = forward(x, y)
223 x = x * scale + offx
224 y = -y * scale + offy
225 width, height = self.dc.GetTextExtent(text)
226 if label.halign == ALIGN_LEFT:
227 # nothing to be done
228 pass
229 elif label.halign == ALIGN_RIGHT:
230 x = x - width
231 elif label.halign == ALIGN_CENTER:
232 x = x - width/2
233 if label.valign == ALIGN_TOP:
234 # nothing to be done
235 pass
236 elif label.valign == ALIGN_BOTTOM:
237 y = y - height
238 elif label.valign == ALIGN_CENTER:
239 y = y - height/2
240 self.dc.DrawText(text, x, y)
241
242
243 class ScreenRenderer(MapRenderer):
244
245 # On the screen we want to see only visible layers by default
246 honor_visibility = 1
247
248 def RenderMap(self, map, region, selected_layer, selected_shapes):
249 """Render the map.
250
251 Only the given region (a tuple in window coordinates as returned
252 by a wxrect's asTuple method) needs to be redrawn. Highlight the
253 shapes given by the ids in selected_shapes in the
254 selected_layer.
255 """
256 self.update_region = region
257 self.selected_layer = selected_layer
258 self.selected_shapes = selected_shapes
259 self.render_map(map)
260
261 def draw_shape_layer(self, layer):
262 MapRenderer.draw_shape_layer(self, layer)
263 if layer is self.selected_layer and self.selected_shapes:
264 pen = wxPen(wxBLACK, 3, wxSOLID)
265 brush = wxBrush(wxBLACK, wxCROSS_HATCH)
266
267 shapetype = layer.ShapeType()
268 if shapetype == SHAPETYPE_POLYGON:
269 offx, offy = self.offset
270 renderparam = self.polygon_render_param(layer)
271 func = self.draw_polygon_shape
272 args = (pen, brush)
273 elif shapetype == SHAPETYPE_ARC:
274 renderparam = self.polygon_render_param(layer)
275 func = self.draw_polygon_shape
276 args = (pen, None)
277 elif shapetype == SHAPETYPE_POINT:
278 renderparam = layer
279 self.dc.SetBrush(brush)
280 self.dc.SetPen(pen)
281 func = self.draw_point_shape
282 args = ()
283 else:
284 raise TypeError(_("Unhandled shape type %s") % shapetype)
285
286 for index in self.selected_shapes:
287 func(renderparam, index, *args)
288
289
290 def layer_ids(self, layer):
291 """Return the shapeids covered by the region that has to be redrawn
292
293 Call the layer's ShapesInRegion method to determine the ids so
294 that it can use the quadtree.
295 """
296 # FIXME: the quad-tree should be built from the projected
297 # coordinates not the lat-long ones because it's not trivial to
298 # determine an appropriate rectangle in lat-long for a given
299 # rectangle in projected coordinates which we have to start from
300 # here.
301 proj = self.map.projection
302 if proj is not None:
303 inverse = proj.Inverse
304 else:
305 inverse = None
306
307 scale = self.scale
308 offx, offy = self.offset
309 xs = []
310 ys = []
311 x, y, width, height = self.update_region
312 for winx, winy in ((x, y), (x + width, y),
313 (x + width, y + height), (x, y + height)):
314 px = (winx - offx) / scale
315 py = -(winy - offy) / scale
316 if inverse:
317 px, py = inverse(px, py)
318 xs.append(px)
319 ys.append(py)
320 left = min(xs)
321 right = max(xs)
322 top = max(ys)
323 bottom = min(ys)
324
325 return layer.ShapesInRegion((left, bottom, right, top))
326
327
328 class PrinterRender(MapRenderer):
329
330 # When printing we want to see all layers
331 honor_visibility = 0
332
333 RenderMap = MapRenderer.render_map
334

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26