/[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 965 - (hide annotations)
Wed May 21 17:24:22 2003 UTC (21 years, 9 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 18706 byte(s)
(MapRenderer.render_map): Check if scale
        has gone to 0 which is a serious problem. abort.
(MapRenderer.draw_raster_layer): Catch IOError seperately and
        print the error from GDAL.

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 bh 6
39     class MapRenderer:
40    
41     """Class to render a map onto a wxDC"""
42    
43     honor_visibility = 1
44    
45     def __init__(self, dc, scale, offset, resolution = 72.0,
46     honor_visibility = None):
47     """Inititalize the renderer.
48    
49     dc -- the wxPython DC to render on
50     scale, offset -- the scale factor and translation to convert
51     between projected coordinates and the DC coordinates
52    
53     resolution -- the assumed resolution of the DC. Used to convert
54     absolute lengths like font sizes to DC coordinates
55    
56     honor_visibility -- boolean. If true, honor the visibility flag
57     of the layers, otherwise draw all layers. If None, use
58     the renderer's default.
59     """
60     # resolution in pixel/inch
61 jonathan 574
62 bh 6 self.dc = dc
63     self.scale = scale
64     self.offset = offset
65     if honor_visibility is not None:
66     self.honor_visibility = honor_visibility
67     # store the resolution in pixel/point because it's more useful
68     # later.
69     self.resolution = resolution / 72.0
70    
71     def render_map(self, map):
72     self.map = map
73 jonathan 938 seenRaster = True
74    
75 jonathan 965 if self.scale == 0:
76     return
77    
78 jonathan 938 #
79     # This is only a good optimization if there is only one
80     # raster layer and the image covers the entire window (as
81     # it currently does).
82     #
83 bh 6 for layer in map.Layers():
84 jonathan 938 if isinstance(layer, RasterLayer):
85     seenRaster = False
86     break
87    
88     for layer in map.Layers():
89 bh 6 # if honor_visibility is true, only draw visible layers,
90     # otherwise draw all layers
91     if not self.honor_visibility or layer.Visible():
92 jonathan 938 if isinstance(layer, Layer) and seenRaster:
93     self.draw_shape_layer(layer)
94     elif isinstance(layer, RasterLayer):
95     self.draw_raster_layer(layer)
96     seenRaster = True
97    
98 bh 6 self.draw_label_layer(map.LabelLayer())
99    
100     def draw_shape_layer(self, layer):
101     scale = self.scale
102     offx, offy = self.offset
103    
104     map_proj = self.map.projection
105     layer_proj = layer.projection
106    
107     shapetype = layer.ShapeType()
108    
109 jonathan 362 brush = wxTRANSPARENT_BRUSH
110     pen = wxTRANSPARENT_PEN
111    
112     old_prop = None
113 jonathan 798 old_group = None
114 jonathan 468 lc = layer.GetClassification()
115     field = lc.GetField()
116 jonathan 798 defaultGroup = lc.GetDefaultGroup()
117 jonathan 468
118 jonathan 676
119     if shapetype != SHAPETYPE_POINT:
120 bh 686 polygon_render_param = self.polygon_render_param(layer)
121 jonathan 676
122 jonathan 798 if shapetype == SHAPETYPE_POINT:
123     draw_func = lambda i: \
124     self.draw_point_shape(layer, i)
125     else:
126     draw_func = lambda i: \
127     self.draw_polygon_shape(polygon_render_param, i, pen, brush)
128    
129 jonathan 362 for i in self.layer_ids(layer):
130    
131 jonathan 798 if field is None:
132     group = defaultGroup
133 jonathan 468 else:
134 bh 837 record = layer.table.ReadRowAsDict(i)
135 jonathan 798 assert record is not None
136     group = lc.FindGroup(record[field])
137 jonathan 362
138 jonathan 642
139     if not group.IsVisible():
140     continue
141    
142 jonathan 676
143 jonathan 442 # don't recreate new objects if they are the same as before
144 jonathan 798 if group is not old_group:
145     old_group = group
146 jonathan 362
147 jonathan 798 prop = group.GetProperties()
148 jonathan 676
149 jonathan 798 if prop != old_prop:
150     old_prop = prop
151 bh 686
152 jonathan 798 if shapetype == SHAPETYPE_ARC:
153     fill = Color.Transparent
154     else:
155     fill = prop.GetFill()
156 bh 686
157 jonathan 676
158 jonathan 798 if fill is Color.Transparent:
159     brush = wxTRANSPARENT_BRUSH
160     else:
161     color = Color2wxColour(fill)
162     brush = wxBrush(color, wxSOLID)
163 bh 686
164 jonathan 798 stroke = prop.GetLineColor()
165     stroke_width = prop.GetLineWidth()
166     if stroke is Color.Transparent:
167     pen = wxTRANSPARENT_PEN
168     else:
169     color = Color2wxColour(stroke)
170     pen = wxPen(color, stroke_width, wxSOLID)
171 bh 6
172 jonathan 798 if shapetype == SHAPETYPE_POINT:
173     self.dc.SetBrush(brush)
174     self.dc.SetPen(pen)
175    
176     draw_func(i)
177    
178 jonathan 938 def draw_raster_layer(self, layer):
179     data = None
180     offx, offy = self.offset
181     width, height = self.dc.GetSizeTuple()
182    
183     inProj = ""
184     proj = layer.GetProjection()
185     if proj is not None:
186     for p in proj.GetAllParameters():
187     inProj += "+" + p + " "
188    
189     outProj = ""
190     proj = self.map.GetProjection()
191     if proj is not None:
192     for p in proj.GetAllParameters():
193     outProj += "+" + p + " "
194    
195     xmin = (0 - offx) / self.scale
196     ymin = (offy - height) / self.scale
197     xmax = (width - offx) / self.scale
198     ymax = (offy - 0) / self.scale
199    
200     try:
201     data = ProjectRasterFile(
202     layer.GetImageFilename(),
203     inProj,
204     outProj,
205     str(xmin), str(ymin), str(xmax), str(ymax),
206     "", str(width), str(height));
207 jonathan 965 except IOError, (strerr):
208     print strerr
209     except (AttributeError, ValueError):
210 jonathan 938 pass
211     else:
212     if data is not None:
213     stream = cStringIO.StringIO(data)
214     image = wxImageFromStream(stream, wxBITMAP_TYPE_BMP)
215     bitmap = wxBitmapFromImage(image)
216     self.dc.BeginDrawing()
217     self.dc.DrawBitmap(bitmap, 0, 0)
218     self.dc.EndDrawing()
219    
220 bh 144 def layer_ids(self, layer):
221     """Return the shape ids of the given layer that have to be drawn.
222 bh 686
223 bh 144 The default implementation simply returns all ids in the layer.
224     Override in derived classes to be more precise.
225     """
226     return range(layer.NumShapes())
227    
228 bh 686 def polygon_render_param(self, layer):
229     """Return the low-lever render parameter for the layer"""
230     offx, offy = self.offset
231     return draw_polygon_init(layer.shapefile, self.dc,
232     self.map.projection,
233     layer.projection,
234     self.scale, -self.scale,
235     offx, offy)
236 bh 6
237 bh 686 def draw_polygon_shape(self, draw_polygon_info, index, pen, brush):
238     draw_polygon_shape(draw_polygon_info, index, pen, brush)
239    
240 bh 6 def projected_points(self, layer, index):
241 jonathan 798 proj = self.map.GetProjection()
242 bh 6 if proj is not None:
243     forward = proj.Forward
244     else:
245     forward = None
246 jonathan 798 proj = layer.GetProjection()
247 bh 6 if proj is not None:
248     inverse = proj.Inverse
249     else:
250     inverse = None
251     shape = layer.Shape(index)
252     points = []
253     scale = self.scale
254     offx, offy = self.offset
255     for x, y in shape.Points():
256     if inverse:
257     x, y = inverse(x, y)
258     if forward:
259     x, y = forward(x, y)
260     points.append(wxPoint(x * scale + offx,
261     -y * scale + offy))
262     return points
263    
264     def draw_arc_shape(self, layer, index):
265     points = self.projected_points(layer, index)
266     self.dc.DrawLines(points)
267    
268     def draw_point_shape(self, layer, index):
269 jonathan 588 pp = self.projected_points(layer, index)
270    
271     if len(pp) == 0: return # ignore Null Shapes which have no points
272    
273     p = pp[0]
274 bh 6 radius = self.resolution * 5
275     self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)
276    
277     def draw_label_layer(self, layer):
278     scale = self.scale
279     offx, offy = self.offset
280    
281     font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
282     self.dc.SetFont(font)
283    
284     map_proj = self.map.projection
285     if map_proj is not None:
286     forward = map_proj.Forward
287     else:
288     forward = None
289    
290     for label in layer.Labels():
291     x = label.x
292     y = label.y
293     text = label.text
294     if forward:
295     x, y = forward(x, y)
296     x = x * scale + offx
297     y = -y * scale + offy
298     width, height = self.dc.GetTextExtent(text)
299     if label.halign == ALIGN_LEFT:
300     # nothing to be done
301     pass
302     elif label.halign == ALIGN_RIGHT:
303     x = x - width
304     elif label.halign == ALIGN_CENTER:
305     x = x - width/2
306     if label.valign == ALIGN_TOP:
307     # nothing to be done
308     pass
309     elif label.valign == ALIGN_BOTTOM:
310     y = y - height
311     elif label.valign == ALIGN_CENTER:
312     y = y - height/2
313     self.dc.DrawText(text, x, y)
314    
315    
316     class ScreenRenderer(MapRenderer):
317    
318     # On the screen we want to see only visible layers by default
319     honor_visibility = 1
320    
321 bh 535 def RenderMap(self, map, region, selected_layer, selected_shapes):
322 bh 144 """Render the map.
323    
324 bh 148 Only the given region (a tuple in window coordinates as returned
325     by a wxrect's asTuple method) needs to be redrawn. Highlight the
326 bh 535 shapes given by the ids in selected_shapes in the
327     selected_layer.
328 bh 144 """
329     self.update_region = region
330 bh 6 self.selected_layer = selected_layer
331 bh 535 self.selected_shapes = selected_shapes
332 bh 6 self.render_map(map)
333    
334     def draw_shape_layer(self, layer):
335     MapRenderer.draw_shape_layer(self, layer)
336 bh 535 if layer is self.selected_layer and self.selected_shapes:
337 bh 6 pen = wxPen(wxBLACK, 3, wxSOLID)
338     brush = wxBrush(wxBLACK, wxCROSS_HATCH)
339 bh 535
340 bh 6 shapetype = layer.ShapeType()
341     if shapetype == SHAPETYPE_POLYGON:
342 bh 686 offx, offy = self.offset
343     renderparam = self.polygon_render_param(layer)
344 bh 535 func = self.draw_polygon_shape
345     args = (pen, brush)
346 bh 290 elif shapetype == SHAPETYPE_ARC:
347 bh 686 renderparam = self.polygon_render_param(layer)
348 bh 535 func = self.draw_polygon_shape
349     args = (pen, None)
350     elif shapetype == SHAPETYPE_POINT:
351 bh 686 renderparam = layer
352 bh 6 self.dc.SetBrush(brush)
353     self.dc.SetPen(pen)
354 bh 535 func = self.draw_point_shape
355     args = ()
356     else:
357     raise TypeError(_("Unhandled shape type %s") % shapetype)
358 bh 6
359 bh 535 for index in self.selected_shapes:
360 bh 686 func(renderparam, index, *args)
361 bh 535
362 bh 686
363 bh 144 def layer_ids(self, layer):
364     """Return the shapeids covered by the region that has to be redrawn
365 bh 6
366 bh 144 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 bh 148 x, y, width, height = self.update_region
385 bh 144 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 frank 909 class ExportRenderer(ScreenRenderer):
402 bh 6
403 frank 909 honor_visibility = 1
404    
405     def RenderMap(self, map, region, mapregion,
406     selected_layer, selected_shapes ):
407     """Render the map.
408 bh 6
409 frank 909 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     for l in map.Layers():
511     if l.Visible():
512     # Render title
513     dc.DrawText(l.Title(), posx, posy)
514     posy+=stepy
515     # Render classification
516     clazz = l.GetClassification()
517     shapeType = l.ShapeType()
518     for g in clazz:
519     if g.IsVisible():
520     previewer.Draw(dc,
521     wxRect(posx+dx, posy, iconwidth, iconheight),
522     g.GetProperties(), shapeType)
523     dc.DrawText(g.GetDisplayText(),
524     posx+2*dx+iconwidth, posy)
525     posy+=stepy
526    
527     def render_scalebar(self, map):
528     """Render the scalebar."""
529    
530     scalebar = ScaleBar(map)
531    
532     # Dimension stuff
533     width, height = self.dc.GetSizeTuple()
534     mminx, mminy, mmaxx, mmaxy = self.mapregion
535    
536     # Render the scalebar
537     scalebar.DrawScaleBar(self.scale, self.dc,
538     (mmaxx+10+5, mmaxy-25),
539     ((width-15-5) - (mmaxx+10+5),20)
540     )
541     # 10 pix between map and legend frame, 5 pix inside legend frame
542     # 25 pix from the legend frame bottom line
543     # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
544     # Height: 20
545    
546     class PrinterRenderer(ExportRenderer):
547    
548     # Printing as well as Export / Screen display only the visible layer.
549     honor_visibility = 1
550    

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26