/[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 1539 - (hide annotations)
Fri Aug 1 14:27:57 2003 UTC (21 years, 7 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 19037 byte(s)
Import the SHAPETYPE_* constants from data
instead of layer.

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 bh 1539 from Thuban.Model.layer import Layer, RasterLayer
31     from Thuban.Model.data import 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 535 func = self.draw_point_shape
356 jonathan 1467 args = (pen, brush)
357 bh 535 else:
358     raise TypeError(_("Unhandled shape type %s") % shapetype)
359 bh 6
360 bh 535 for index in self.selected_shapes:
361 bh 686 func(renderparam, index, *args)
362 bh 535
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 frank 1324 layers = map.Layers()
511     layers.reverse()
512     for l in layers:
513 frank 909 if l.Visible():
514     # Render title
515     dc.DrawText(l.Title(), posx, posy)
516     posy+=stepy
517 jonathan 1298 if l.HasClassification():
518     # Render classification
519     clazz = l.GetClassification()
520     shapeType = l.ShapeType()
521     for g in clazz:
522     if g.IsVisible():
523     previewer.Draw(dc,
524     wxRect(posx+dx, posy,
525     iconwidth, iconheight),
526     g.GetProperties(), shapeType)
527     dc.DrawText(g.GetDisplayText(),
528     posx+2*dx+iconwidth, posy)
529     posy+=stepy
530 frank 909
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