/[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 1234 - (hide annotations)
Wed Jun 18 14:47:38 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: 19116 byte(s)
Check for gdal support before importing gdalwarp.
(MapRenderer.render_map): Only try to optimize if we have gdal
        support otherwise nothing will get drawn.
(MapCanvas.FitMapToWindow): This may be called during startup
        before a map has been created. Check if map is None before
        using it and do nothing if it is.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26