/[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 1467 - (hide annotations)
Tue Jul 22 14:02:57 2003 UTC (21 years, 7 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 19091 byte(s)
(MapRenderer.render_map): Wrap method
        with Begin/EndDrawing() calls to ensure we aren't doing to
        many updates to the dc during rendering.
(ScreenRenderer.draw_shape_layer): self.draw_point_shape takes
        a pen and brush argument so they need to be passed to the function.

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 frank 909 # Frank Koormann <[email protected]>
6 bh 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 jonathan 938 import cStringIO
13    
14 jonathan 882 from Thuban import _
15    
16 frank 909 from wxPython.wx import wxMemoryDC, wxEmptyBitmap, \
17     wxPoint, wxRect, wxPen, wxBrush, wxFont, \
18     wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
19 jonathan 938 wxBLACK_PEN, wxRED_PEN, wxBLACK, \
20     wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
21     wxBitmap, wxImageFromBitmap, wxBitmapFromImage, \
22     wxImageFromStream, wxBITMAP_TYPE_BMP
23 bh 6
24 jonathan 676 from wxproj import draw_polygon_shape, draw_polygon_init
25 bh 6
26 jonathan 882 from Thuban.UI.common import Color2wxColour
27 frank 909 from Thuban.UI.classifier import ClassDataPreviewer
28     from Thuban.UI.scalebar import ScaleBar
29 jan 374
30 jonathan 938 from Thuban.Model.layer import Layer, RasterLayer, \
31     SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
32 bh 6 from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
33     ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE
34    
35 jonathan 394 from Thuban.Model.classification import Classification
36 jonathan 1343 from Thuban.Model.color import Transparent
37 jonathan 1165 import Thuban.Model.resource
38 bh 6
39 jonathan 1234 if Thuban.Model.resource.has_gdal_support():
40     from gdalwarp import ProjectRasterFile
41    
42 bh 6 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 jonathan 574
65 bh 6 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 jonathan 938 seenRaster = True
77    
78 jonathan 1467 self.dc.BeginDrawing()
79 jonathan 965
80 jonathan 938 #
81     # This is only a good optimization if there is only one
82     # raster layer and the image covers the entire window (as
83 jonathan 1165 # 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 jonathan 938 #
87 jonathan 1234 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 jonathan 938
93     for layer in map.Layers():
94 bh 6 # if honor_visibility is true, only draw visible layers,
95     # otherwise draw all layers
96     if not self.honor_visibility or layer.Visible():
97 jonathan 938 if isinstance(layer, Layer) and seenRaster:
98     self.draw_shape_layer(layer)
99 jonathan 1165 elif isinstance(layer, RasterLayer) \
100     and Thuban.Model.resource.has_gdal_support():
101 jonathan 938 self.draw_raster_layer(layer)
102     seenRaster = True
103    
104 bh 6 self.draw_label_layer(map.LabelLayer())
105    
106 jonathan 1467 self.dc.EndDrawing()
107    
108 bh 6 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 jonathan 362 brush = wxTRANSPARENT_BRUSH
118     pen = wxTRANSPARENT_PEN
119    
120     old_prop = None
121 jonathan 798 old_group = None
122 jonathan 468 lc = layer.GetClassification()
123 bh 1452 field = layer.GetClassificationColumn()
124 jonathan 798 defaultGroup = lc.GetDefaultGroup()
125 jonathan 468
126 jonathan 676
127 jonathan 1467
128 jonathan 798 if shapetype == SHAPETYPE_POINT:
129 bh 1419 draw_func = self.draw_point_shape
130     draw_func_param = layer
131 jonathan 798 else:
132 bh 1419 draw_func = draw_polygon_shape
133     draw_func_param = self.polygon_render_param(layer)
134 bh 1219
135     table = layer.ShapeStore().Table()
136 jonathan 362 for i in self.layer_ids(layer):
137    
138 jonathan 798 if field is None:
139     group = defaultGroup
140 jonathan 468 else:
141 bh 1219 record = table.ReadRowAsDict(i)
142 jonathan 798 assert record is not None
143     group = lc.FindGroup(record[field])
144 jonathan 362
145 jonathan 642
146     if not group.IsVisible():
147     continue
148    
149 jonathan 676
150 jonathan 442 # don't recreate new objects if they are the same as before
151 jonathan 798 if group is not old_group:
152     old_group = group
153 jonathan 362
154 jonathan 798 prop = group.GetProperties()
155 jonathan 676
156 jonathan 798 if prop != old_prop:
157     old_prop = prop
158 bh 686
159 jonathan 798 if shapetype == SHAPETYPE_ARC:
160 jonathan 1343 fill = Transparent
161 jonathan 798 else:
162     fill = prop.GetFill()
163 bh 686
164 jonathan 676
165 jonathan 1343 if fill is Transparent:
166 jonathan 798 brush = wxTRANSPARENT_BRUSH
167     else:
168     color = Color2wxColour(fill)
169     brush = wxBrush(color, wxSOLID)
170 bh 686
171 jonathan 798 stroke = prop.GetLineColor()
172     stroke_width = prop.GetLineWidth()
173 jonathan 1343 if stroke is Transparent:
174 jonathan 798 pen = wxTRANSPARENT_PEN
175     else:
176     color = Color2wxColour(stroke)
177     pen = wxPen(color, stroke_width, wxSOLID)
178 bh 6
179 bh 1419 draw_func(draw_func_param, i, pen, brush)
180 jonathan 798
181 jonathan 938 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 jonathan 1103 (xmin, ymin, xmax, ymax),
209     "", (width, height))
210 jonathan 965 except IOError, (strerr):
211     print strerr
212     except (AttributeError, ValueError):
213 jonathan 938 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 bh 144 def layer_ids(self, layer):
222     """Return the shape ids of the given layer that have to be drawn.
223 bh 686
224 bh 144 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 bh 686 def polygon_render_param(self, layer):
230     """Return the low-lever render parameter for the layer"""
231     offx, offy = self.offset
232 bh 1219 return draw_polygon_init(layer.ShapeStore().Shapefile(), self.dc,
233 bh 686 self.map.projection,
234     layer.projection,
235     self.scale, -self.scale,
236     offx, offy)
237 bh 6
238 bh 686 def draw_polygon_shape(self, draw_polygon_info, index, pen, brush):
239     draw_polygon_shape(draw_polygon_info, index, pen, brush)
240    
241 bh 6 def projected_points(self, layer, index):
242 jonathan 798 proj = self.map.GetProjection()
243 bh 6 if proj is not None:
244     forward = proj.Forward
245     else:
246     forward = None
247 jonathan 798 proj = layer.GetProjection()
248 bh 6 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 bh 1419 def draw_point_shape(self, layer, index, pen, brush):
270 jonathan 588 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 bh 6 radius = self.resolution * 5
276 bh 1419 self.dc.SetBrush(brush)
277     self.dc.SetPen(pen)
278 bh 6 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 bh 535 def RenderMap(self, map, region, selected_layer, selected_shapes):
325 bh 144 """Render the map.
326    
327 bh 148 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 bh 535 shapes given by the ids in selected_shapes in the
330     selected_layer.
331 bh 144 """
332     self.update_region = region
333 bh 6 self.selected_layer = selected_layer
334 bh 535 self.selected_shapes = selected_shapes
335 bh 6 self.render_map(map)
336    
337     def draw_shape_layer(self, layer):
338     MapRenderer.draw_shape_layer(self, layer)
339 bh 535 if layer is self.selected_layer and self.selected_shapes:
340 bh 6 pen = wxPen(wxBLACK, 3, wxSOLID)
341     brush = wxBrush(wxBLACK, wxCROSS_HATCH)
342 bh 535
343 bh 6 shapetype = layer.ShapeType()
344     if shapetype == SHAPETYPE_POLYGON:
345 bh 686 offx, offy = self.offset
346     renderparam = self.polygon_render_param(layer)
347 bh 535 func = self.draw_polygon_shape
348     args = (pen, brush)
349 bh 290 elif shapetype == SHAPETYPE_ARC:
350 bh 686 renderparam = self.polygon_render_param(layer)
351 bh 535 func = self.draw_polygon_shape
352     args = (pen, None)
353     elif shapetype == SHAPETYPE_POINT:
354 bh 686 renderparam = layer
355 bh 6 self.dc.SetBrush(brush)
356     self.dc.SetPen(pen)
357 bh 535 func = self.draw_point_shape
358 jonathan 1467 args = (pen, brush)
359 bh 535 else:
360     raise TypeError(_("Unhandled shape type %s") % shapetype)
361 bh 6
362 bh 535 for index in self.selected_shapes:
363 bh 686 func(renderparam, index, *args)
364 bh 535
365 bh 144 def layer_ids(self, layer):
366     """Return the shapeids covered by the region that has to be redrawn
367 bh 6
368 bh 144 Call the layer's ShapesInRegion method to determine the ids so
369     that it can use the quadtree.
370     """
371     # FIXME: the quad-tree should be built from the projected
372     # coordinates not the lat-long ones because it's not trivial to
373     # determine an appropriate rectangle in lat-long for a given
374     # rectangle in projected coordinates which we have to start from
375     # here.
376     proj = self.map.projection
377     if proj is not None:
378     inverse = proj.Inverse
379     else:
380     inverse = None
381    
382     scale = self.scale
383     offx, offy = self.offset
384     xs = []
385     ys = []
386 bh 148 x, y, width, height = self.update_region
387 bh 144 for winx, winy in ((x, y), (x + width, y),
388     (x + width, y + height), (x, y + height)):
389     px = (winx - offx) / scale
390     py = -(winy - offy) / scale
391     if inverse:
392     px, py = inverse(px, py)
393     xs.append(px)
394     ys.append(py)
395     left = min(xs)
396     right = max(xs)
397     top = max(ys)
398     bottom = min(ys)
399    
400     return layer.ShapesInRegion((left, bottom, right, top))
401    
402    
403 frank 909 class ExportRenderer(ScreenRenderer):
404 bh 6
405 frank 909 honor_visibility = 1
406    
407     def RenderMap(self, map, region, mapregion,
408     selected_layer, selected_shapes ):
409     """Render the map.
410 bh 6
411 frank 909 The rendering device has been specified during initialisation.
412     The device border distance was set in Thuban.UI.view.OutputTranform().
413    
414     RenderMap renders a frame set (one page frame, one around
415     legend/scalebar and one around the map), the map, the legend and the
416     scalebar on the given DC. The map is rendered with the region displayed
417     in the canvas view, centered on the area available for map display.
418     """
419    
420     self.update_region = region
421     self.selected_layer = selected_layer
422     self.selected_shapes = selected_shapes
423    
424     # Get some dimensions
425     llx, lly, urx, ury = region
426     self.mapregion = mapregion
427     mminx, mminy, mmaxx, mmaxy = self.mapregion
428    
429     # Manipulate the offset to position the map
430     offx, offy = self.offset
431     # 1. Shift to corner of map drawing area
432     offx = offx + mminx
433     offy = offy + mminy
434    
435     # 2. Center the map on the map drawing area:
436     # region identifies the region on the canvas view:
437     # center of map drawing area - half the size of region: rendering origin
438     self.shiftx = (mmaxx - mminx)*0.5 - (urx - llx)*0.5
439     self.shifty = (mmaxy - mminy)*0.5 - (ury - lly)*0.5
440    
441     self.offset = (offx+self.shiftx, offy+self.shifty)
442    
443     # Draw the map
444     self.dc.BeginDrawing()
445     self.dc.DestroyClippingRegion()
446     self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
447     urx, ury)
448     self.render_map(map)
449     self.dc.EndDrawing()
450    
451     # Draw the rest (frames, legend, scalebar)
452     self.dc.BeginDrawing()
453     self.dc.DestroyClippingRegion()
454    
455     # Force the font for Legend drawing
456     font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
457     self.dc.SetFont(font)
458    
459     self.render_frame(region)
460     self.render_legend(map)
461     self.render_scalebar(map)
462     self.dc.EndDrawing()
463    
464     def render_frame(self, region):
465     """Render the frames for map and legend/scalebar."""
466    
467     dc = self.dc
468     dc.SetPen(wxBLACK_PEN)
469     dc.SetBrush(wxTRANSPARENT_BRUSH)
470    
471     # Dimension stuff
472     width, height = dc.GetSizeTuple()
473     mminx, mminy, mmaxx, mmaxy = self.mapregion
474    
475     # Page Frame
476     dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
477    
478     # Map Frame
479     llx, lly, urx, ury = region
480     dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
481    
482     # Legend Frame
483     dc.DrawRectangle(mmaxx+10,mminy,(width-20) - (mmaxx+10), mmaxy-mminy)
484    
485     dc.DestroyClippingRegion()
486     dc.SetClippingRegion(mmaxx+10,mminy,
487     (width-20) - (mmaxx+10), mmaxy-mminy)
488    
489     def render_legend(self, map):
490     """Render the legend on the Map."""
491    
492     previewer = ClassDataPreviewer()
493     dc = self.dc
494     dc.SetPen(wxBLACK_PEN)
495     dc.SetBrush(wxTRANSPARENT_BRUSH)
496    
497     # Dimension stuff
498     width, height = dc.GetSizeTuple()
499     mminx, mminy, mmaxx, mmaxy = self.mapregion
500     textwidth, textheight = dc.GetTextExtent("0")
501     iconwidth = textheight
502     iconheight = textheight
503     stepy = textheight+3
504     dx = 10
505     posx = mmaxx + 10 + 5 # 10 pix distance mapframe/legend frame,
506     # 5 pix inside legend frame
507     posy = mminy + 5 # 5 pix inside legend frame
508    
509     # Render the legend
510     dc.SetTextForeground(wxBLACK)
511     if map.HasLayers():
512 frank 1324 layers = map.Layers()
513     layers.reverse()
514     for l in layers:
515 frank 909 if l.Visible():
516     # Render title
517     dc.DrawText(l.Title(), posx, posy)
518     posy+=stepy
519 jonathan 1298 if l.HasClassification():
520     # Render classification
521     clazz = l.GetClassification()
522     shapeType = l.ShapeType()
523     for g in clazz:
524     if g.IsVisible():
525     previewer.Draw(dc,
526     wxRect(posx+dx, posy,
527     iconwidth, iconheight),
528     g.GetProperties(), shapeType)
529     dc.DrawText(g.GetDisplayText(),
530     posx+2*dx+iconwidth, posy)
531     posy+=stepy
532 frank 909
533     def render_scalebar(self, map):
534     """Render the scalebar."""
535    
536     scalebar = ScaleBar(map)
537    
538     # Dimension stuff
539     width, height = self.dc.GetSizeTuple()
540     mminx, mminy, mmaxx, mmaxy = self.mapregion
541    
542     # Render the scalebar
543     scalebar.DrawScaleBar(self.scale, self.dc,
544     (mmaxx+10+5, mmaxy-25),
545     ((width-15-5) - (mmaxx+10+5),20)
546     )
547     # 10 pix between map and legend frame, 5 pix inside legend frame
548     # 25 pix from the legend frame bottom line
549     # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
550     # Height: 20
551    
552     class PrinterRenderer(ExportRenderer):
553    
554     # Printing as well as Export / Screen display only the visible layer.
555     honor_visibility = 1
556    

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26