/[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 938 - (hide annotations)
Tue May 20 15:25:10 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: 18676 byte(s)
(MapRenderer.render_map): Add support for rendering RasterLayer layers.
(MapRenderer.draw_raster_layer): Actually method that calls
        the GDALWarp python wrapper and constructs an image from the
        data returned.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26