/[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 1165 - (hide annotations)
Thu Jun 12 12:42:10 2003 UTC (21 years, 8 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 18957 byte(s)
(MapRenderer.render_map): Only try to
        optimize if a raster layer is visible. Fixes RTbug #1931.
        Only draw the raster layer if the gdal library is available.
        Addresses RTbug #1877.

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