/[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 1539 - (show annotations)
Fri Aug 1 14:27:57 2003 UTC (21 years, 7 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 19037 byte(s)
Import the SHAPETYPE_* constants from data
instead of layer.

1 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Bernhard Herzog <[email protected]>
4 # Jonathan Coles <[email protected]>
5 # Frank Koormann <[email protected]>
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 import cStringIO
13
14 from Thuban import _
15
16 from wxPython.wx import wxMemoryDC, wxEmptyBitmap, \
17 wxPoint, wxRect, wxPen, wxBrush, wxFont, \
18 wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
19 wxBLACK_PEN, wxRED_PEN, wxBLACK, \
20 wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
21 wxBitmap, wxImageFromBitmap, wxBitmapFromImage, \
22 wxImageFromStream, wxBITMAP_TYPE_BMP
23
24 from wxproj import draw_polygon_shape, draw_polygon_init
25
26 from Thuban.UI.common import Color2wxColour
27 from Thuban.UI.classifier import ClassDataPreviewer
28 from Thuban.UI.scalebar import ScaleBar
29
30 from Thuban.Model.layer import Layer, RasterLayer
31 from Thuban.Model.data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
32 from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
33 ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE
34
35 from Thuban.Model.classification import Classification
36 from Thuban.Model.color import Transparent
37 import Thuban.Model.resource
38
39 if Thuban.Model.resource.has_gdal_support():
40 from gdalwarp import ProjectRasterFile
41
42 class MapRenderer:
43
44 """Class to render a map onto a wxDC"""
45
46 honor_visibility = 1
47
48 def __init__(self, dc, scale, offset, resolution = 72.0,
49 honor_visibility = None):
50 """Inititalize the renderer.
51
52 dc -- the wxPython DC to render on
53 scale, offset -- the scale factor and translation to convert
54 between projected coordinates and the DC coordinates
55
56 resolution -- the assumed resolution of the DC. Used to convert
57 absolute lengths like font sizes to DC coordinates
58
59 honor_visibility -- boolean. If true, honor the visibility flag
60 of the layers, otherwise draw all layers. If None, use
61 the renderer's default.
62 """
63 # resolution in pixel/inch
64
65 self.dc = dc
66 self.scale = scale
67 self.offset = offset
68 if honor_visibility is not None:
69 self.honor_visibility = honor_visibility
70 # store the resolution in pixel/point because it's more useful
71 # later.
72 self.resolution = resolution / 72.0
73
74 def render_map(self, map):
75 self.map = map
76 seenRaster = True
77
78 self.dc.BeginDrawing()
79
80 #
81 # This is only a good optimization if there is only one
82 # raster layer and the image covers the entire window (as
83 # it currently does). We note if there is a raster layer
84 # and only begin drawing layers once we have drawn it.
85 # That way we avoid drawing layers that won't be seen.
86 #
87 if Thuban.Model.resource.has_gdal_support():
88 for layer in map.Layers():
89 if isinstance(layer, RasterLayer) and layer.Visible():
90 seenRaster = False
91 break
92
93 for layer in map.Layers():
94 # if honor_visibility is true, only draw visible layers,
95 # otherwise draw all layers
96 if not self.honor_visibility or layer.Visible():
97 if isinstance(layer, Layer) and seenRaster:
98 self.draw_shape_layer(layer)
99 elif isinstance(layer, RasterLayer) \
100 and Thuban.Model.resource.has_gdal_support():
101 self.draw_raster_layer(layer)
102 seenRaster = True
103
104 self.draw_label_layer(map.LabelLayer())
105
106 self.dc.EndDrawing()
107
108 def draw_shape_layer(self, layer):
109 scale = self.scale
110 offx, offy = self.offset
111
112 map_proj = self.map.projection
113 layer_proj = layer.projection
114
115 shapetype = layer.ShapeType()
116
117 brush = wxTRANSPARENT_BRUSH
118 pen = wxTRANSPARENT_PEN
119
120 old_prop = None
121 old_group = None
122 lc = layer.GetClassification()
123 field = layer.GetClassificationColumn()
124 defaultGroup = lc.GetDefaultGroup()
125
126
127
128 if shapetype == SHAPETYPE_POINT:
129 draw_func = self.draw_point_shape
130 draw_func_param = layer
131 else:
132 draw_func = draw_polygon_shape
133 draw_func_param = self.polygon_render_param(layer)
134
135 table = layer.ShapeStore().Table()
136 for i in self.layer_ids(layer):
137
138 if field is None:
139 group = defaultGroup
140 else:
141 record = table.ReadRowAsDict(i)
142 assert record is not None
143 group = lc.FindGroup(record[field])
144
145
146 if not group.IsVisible():
147 continue
148
149
150 # don't recreate new objects if they are the same as before
151 if group is not old_group:
152 old_group = group
153
154 prop = group.GetProperties()
155
156 if prop != old_prop:
157 old_prop = prop
158
159 if shapetype == SHAPETYPE_ARC:
160 fill = Transparent
161 else:
162 fill = prop.GetFill()
163
164
165 if fill is Transparent:
166 brush = wxTRANSPARENT_BRUSH
167 else:
168 color = Color2wxColour(fill)
169 brush = wxBrush(color, wxSOLID)
170
171 stroke = prop.GetLineColor()
172 stroke_width = prop.GetLineWidth()
173 if stroke is Transparent:
174 pen = wxTRANSPARENT_PEN
175 else:
176 color = Color2wxColour(stroke)
177 pen = wxPen(color, stroke_width, wxSOLID)
178
179 draw_func(draw_func_param, i, pen, brush)
180
181 def draw_raster_layer(self, layer):
182 data = None
183 offx, offy = self.offset
184 width, height = self.dc.GetSizeTuple()
185
186 inProj = ""
187 proj = layer.GetProjection()
188 if proj is not None:
189 for p in proj.GetAllParameters():
190 inProj += "+" + p + " "
191
192 outProj = ""
193 proj = self.map.GetProjection()
194 if proj is not None:
195 for p in proj.GetAllParameters():
196 outProj += "+" + p + " "
197
198 xmin = (0 - offx) / self.scale
199 ymin = (offy - height) / self.scale
200 xmax = (width - offx) / self.scale
201 ymax = (offy - 0) / self.scale
202
203 try:
204 data = ProjectRasterFile(
205 layer.GetImageFilename(),
206 inProj,
207 outProj,
208 (xmin, ymin, xmax, ymax),
209 "", (width, height))
210 except IOError, (strerr):
211 print strerr
212 except (AttributeError, ValueError):
213 pass
214 else:
215 if data is not None:
216 stream = cStringIO.StringIO(data)
217 image = wxImageFromStream(stream, wxBITMAP_TYPE_BMP)
218 bitmap = wxBitmapFromImage(image)
219 self.dc.DrawBitmap(bitmap, 0, 0)
220
221 def layer_ids(self, layer):
222 """Return the shape ids of the given layer that have to be drawn.
223
224 The default implementation simply returns all ids in the layer.
225 Override in derived classes to be more precise.
226 """
227 return range(layer.NumShapes())
228
229 def polygon_render_param(self, layer):
230 """Return the low-lever render parameter for the layer"""
231 offx, offy = self.offset
232 return draw_polygon_init(layer.ShapeStore().Shapefile(), self.dc,
233 self.map.projection,
234 layer.projection,
235 self.scale, -self.scale,
236 offx, offy)
237
238 def draw_polygon_shape(self, draw_polygon_info, index, pen, brush):
239 draw_polygon_shape(draw_polygon_info, index, pen, brush)
240
241 def projected_points(self, layer, index):
242 proj = self.map.GetProjection()
243 if proj is not None:
244 forward = proj.Forward
245 else:
246 forward = None
247 proj = layer.GetProjection()
248 if proj is not None:
249 inverse = proj.Inverse
250 else:
251 inverse = None
252 shape = layer.Shape(index)
253 points = []
254 scale = self.scale
255 offx, offy = self.offset
256 for x, y in shape.Points():
257 if inverse:
258 x, y = inverse(x, y)
259 if forward:
260 x, y = forward(x, y)
261 points.append(wxPoint(x * scale + offx,
262 -y * scale + offy))
263 return points
264
265 def draw_arc_shape(self, layer, index):
266 points = self.projected_points(layer, index)
267 self.dc.DrawLines(points)
268
269 def draw_point_shape(self, layer, index, pen, brush):
270 pp = self.projected_points(layer, index)
271
272 if len(pp) == 0: return # ignore Null Shapes which have no points
273
274 p = pp[0]
275 radius = self.resolution * 5
276 self.dc.SetBrush(brush)
277 self.dc.SetPen(pen)
278 self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)
279
280 def draw_label_layer(self, layer):
281 scale = self.scale
282 offx, offy = self.offset
283
284 font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
285 self.dc.SetFont(font)
286
287 map_proj = self.map.projection
288 if map_proj is not None:
289 forward = map_proj.Forward
290 else:
291 forward = None
292
293 for label in layer.Labels():
294 x = label.x
295 y = label.y
296 text = label.text
297 if forward:
298 x, y = forward(x, y)
299 x = x * scale + offx
300 y = -y * scale + offy
301 width, height = self.dc.GetTextExtent(text)
302 if label.halign == ALIGN_LEFT:
303 # nothing to be done
304 pass
305 elif label.halign == ALIGN_RIGHT:
306 x = x - width
307 elif label.halign == ALIGN_CENTER:
308 x = x - width/2
309 if label.valign == ALIGN_TOP:
310 # nothing to be done
311 pass
312 elif label.valign == ALIGN_BOTTOM:
313 y = y - height
314 elif label.valign == ALIGN_CENTER:
315 y = y - height/2
316 self.dc.DrawText(text, x, y)
317
318
319 class ScreenRenderer(MapRenderer):
320
321 # On the screen we want to see only visible layers by default
322 honor_visibility = 1
323
324 def RenderMap(self, map, region, selected_layer, selected_shapes):
325 """Render the map.
326
327 Only the given region (a tuple in window coordinates as returned
328 by a wxrect's asTuple method) needs to be redrawn. Highlight the
329 shapes given by the ids in selected_shapes in the
330 selected_layer.
331 """
332 self.update_region = region
333 self.selected_layer = selected_layer
334 self.selected_shapes = selected_shapes
335 self.render_map(map)
336
337 def draw_shape_layer(self, layer):
338 MapRenderer.draw_shape_layer(self, layer)
339 if layer is self.selected_layer and self.selected_shapes:
340 pen = wxPen(wxBLACK, 3, wxSOLID)
341 brush = wxBrush(wxBLACK, wxCROSS_HATCH)
342
343 shapetype = layer.ShapeType()
344 if shapetype == SHAPETYPE_POLYGON:
345 offx, offy = self.offset
346 renderparam = self.polygon_render_param(layer)
347 func = self.draw_polygon_shape
348 args = (pen, brush)
349 elif shapetype == SHAPETYPE_ARC:
350 renderparam = self.polygon_render_param(layer)
351 func = self.draw_polygon_shape
352 args = (pen, None)
353 elif shapetype == SHAPETYPE_POINT:
354 renderparam = layer
355 func = self.draw_point_shape
356 args = (pen, brush)
357 else:
358 raise TypeError(_("Unhandled shape type %s") % shapetype)
359
360 for index in self.selected_shapes:
361 func(renderparam, index, *args)
362
363 def layer_ids(self, layer):
364 """Return the shapeids covered by the region that has to be redrawn
365
366 Call the layer's ShapesInRegion method to determine the ids so
367 that it can use the quadtree.
368 """
369 # FIXME: the quad-tree should be built from the projected
370 # coordinates not the lat-long ones because it's not trivial to
371 # determine an appropriate rectangle in lat-long for a given
372 # rectangle in projected coordinates which we have to start from
373 # here.
374 proj = self.map.projection
375 if proj is not None:
376 inverse = proj.Inverse
377 else:
378 inverse = None
379
380 scale = self.scale
381 offx, offy = self.offset
382 xs = []
383 ys = []
384 x, y, width, height = self.update_region
385 for winx, winy in ((x, y), (x + width, y),
386 (x + width, y + height), (x, y + height)):
387 px = (winx - offx) / scale
388 py = -(winy - offy) / scale
389 if inverse:
390 px, py = inverse(px, py)
391 xs.append(px)
392 ys.append(py)
393 left = min(xs)
394 right = max(xs)
395 top = max(ys)
396 bottom = min(ys)
397
398 return layer.ShapesInRegion((left, bottom, right, top))
399
400
401 class ExportRenderer(ScreenRenderer):
402
403 honor_visibility = 1
404
405 def RenderMap(self, map, region, mapregion,
406 selected_layer, selected_shapes ):
407 """Render the map.
408
409 The rendering device has been specified during initialisation.
410 The device border distance was set in Thuban.UI.view.OutputTranform().
411
412 RenderMap renders a frame set (one page frame, one around
413 legend/scalebar and one around the map), the map, the legend and the
414 scalebar on the given DC. The map is rendered with the region displayed
415 in the canvas view, centered on the area available for map display.
416 """
417
418 self.update_region = region
419 self.selected_layer = selected_layer
420 self.selected_shapes = selected_shapes
421
422 # Get some dimensions
423 llx, lly, urx, ury = region
424 self.mapregion = mapregion
425 mminx, mminy, mmaxx, mmaxy = self.mapregion
426
427 # Manipulate the offset to position the map
428 offx, offy = self.offset
429 # 1. Shift to corner of map drawing area
430 offx = offx + mminx
431 offy = offy + mminy
432
433 # 2. Center the map on the map drawing area:
434 # region identifies the region on the canvas view:
435 # center of map drawing area - half the size of region: rendering origin
436 self.shiftx = (mmaxx - mminx)*0.5 - (urx - llx)*0.5
437 self.shifty = (mmaxy - mminy)*0.5 - (ury - lly)*0.5
438
439 self.offset = (offx+self.shiftx, offy+self.shifty)
440
441 # Draw the map
442 self.dc.BeginDrawing()
443 self.dc.DestroyClippingRegion()
444 self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
445 urx, ury)
446 self.render_map(map)
447 self.dc.EndDrawing()
448
449 # Draw the rest (frames, legend, scalebar)
450 self.dc.BeginDrawing()
451 self.dc.DestroyClippingRegion()
452
453 # Force the font for Legend drawing
454 font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
455 self.dc.SetFont(font)
456
457 self.render_frame(region)
458 self.render_legend(map)
459 self.render_scalebar(map)
460 self.dc.EndDrawing()
461
462 def render_frame(self, region):
463 """Render the frames for map and legend/scalebar."""
464
465 dc = self.dc
466 dc.SetPen(wxBLACK_PEN)
467 dc.SetBrush(wxTRANSPARENT_BRUSH)
468
469 # Dimension stuff
470 width, height = dc.GetSizeTuple()
471 mminx, mminy, mmaxx, mmaxy = self.mapregion
472
473 # Page Frame
474 dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
475
476 # Map Frame
477 llx, lly, urx, ury = region
478 dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
479
480 # Legend Frame
481 dc.DrawRectangle(mmaxx+10,mminy,(width-20) - (mmaxx+10), mmaxy-mminy)
482
483 dc.DestroyClippingRegion()
484 dc.SetClippingRegion(mmaxx+10,mminy,
485 (width-20) - (mmaxx+10), mmaxy-mminy)
486
487 def render_legend(self, map):
488 """Render the legend on the Map."""
489
490 previewer = ClassDataPreviewer()
491 dc = self.dc
492 dc.SetPen(wxBLACK_PEN)
493 dc.SetBrush(wxTRANSPARENT_BRUSH)
494
495 # Dimension stuff
496 width, height = dc.GetSizeTuple()
497 mminx, mminy, mmaxx, mmaxy = self.mapregion
498 textwidth, textheight = dc.GetTextExtent("0")
499 iconwidth = textheight
500 iconheight = textheight
501 stepy = textheight+3
502 dx = 10
503 posx = mmaxx + 10 + 5 # 10 pix distance mapframe/legend frame,
504 # 5 pix inside legend frame
505 posy = mminy + 5 # 5 pix inside legend frame
506
507 # Render the legend
508 dc.SetTextForeground(wxBLACK)
509 if map.HasLayers():
510 layers = map.Layers()
511 layers.reverse()
512 for l in layers:
513 if l.Visible():
514 # Render title
515 dc.DrawText(l.Title(), posx, posy)
516 posy+=stepy
517 if l.HasClassification():
518 # Render classification
519 clazz = l.GetClassification()
520 shapeType = l.ShapeType()
521 for g in clazz:
522 if g.IsVisible():
523 previewer.Draw(dc,
524 wxRect(posx+dx, posy,
525 iconwidth, iconheight),
526 g.GetProperties(), shapeType)
527 dc.DrawText(g.GetDisplayText(),
528 posx+2*dx+iconwidth, posy)
529 posy+=stepy
530
531 def render_scalebar(self, map):
532 """Render the scalebar."""
533
534 scalebar = ScaleBar(map)
535
536 # Dimension stuff
537 width, height = self.dc.GetSizeTuple()
538 mminx, mminy, mmaxx, mmaxy = self.mapregion
539
540 # Render the scalebar
541 scalebar.DrawScaleBar(self.scale, self.dc,
542 (mmaxx+10+5, mmaxy-25),
543 ((width-15-5) - (mmaxx+10+5),20)
544 )
545 # 10 pix between map and legend frame, 5 pix inside legend frame
546 # 25 pix from the legend frame bottom line
547 # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
548 # Height: 20
549
550 class PrinterRenderer(ExportRenderer):
551
552 # Printing as well as Export / Screen display only the visible layer.
553 honor_visibility = 1
554

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26