/[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 574 - (show annotations)
Fri Mar 28 17:07:26 2003 UTC (21 years, 11 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 10364 byte(s)
(MapRenderer.__init__): Added assert
        to check if scale > 0. Trying to track down a divide by zero.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26