/[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 1434 - (hide annotations)
Wed Jul 16 13:24:39 2003 UTC (21 years, 7 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/renderer.py
File MIME type: text/x-python
File size: 19142 byte(s)
(MapRenderer.draw_shape_layer): Get the classification field from 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 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 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 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 jonathan 1434 field = layer.GetClassificationField()
123 jonathan 798 defaultGroup = lc.GetDefaultGroup()
124 jonathan 468
125 jonathan 676
126 jonathan 798 if shapetype == SHAPETYPE_POINT:
127 bh 1419 draw_func = self.draw_point_shape
128     draw_func_param = layer
129 jonathan 798 else:
130 bh 1419 draw_func = draw_polygon_shape
131     draw_func_param = self.polygon_render_param(layer)
132 bh 1219
133     table = layer.ShapeStore().Table()
134 jonathan 362 for i in self.layer_ids(layer):
135    
136 jonathan 798 if field is None:
137     group = defaultGroup
138 jonathan 468 else:
139 bh 1219 record = table.ReadRowAsDict(i)
140 jonathan 798 assert record is not None
141     group = lc.FindGroup(record[field])
142 jonathan 362
143 jonathan 642
144     if not group.IsVisible():
145     continue
146    
147 jonathan 676
148 jonathan 442 # don't recreate new objects if they are the same as before
149 jonathan 798 if group is not old_group:
150     old_group = group
151 jonathan 362
152 jonathan 798 prop = group.GetProperties()
153 jonathan 676
154 jonathan 798 if prop != old_prop:
155     old_prop = prop
156 bh 686
157 jonathan 798 if shapetype == SHAPETYPE_ARC:
158 jonathan 1343 fill = Transparent
159 jonathan 798 else:
160     fill = prop.GetFill()
161 bh 686
162 jonathan 676
163 jonathan 1343 if fill is Transparent:
164 jonathan 798 brush = wxTRANSPARENT_BRUSH
165     else:
166     color = Color2wxColour(fill)
167     brush = wxBrush(color, wxSOLID)
168 bh 686
169 jonathan 798 stroke = prop.GetLineColor()
170     stroke_width = prop.GetLineWidth()
171 jonathan 1343 if stroke is Transparent:
172 jonathan 798 pen = wxTRANSPARENT_PEN
173     else:
174     color = Color2wxColour(stroke)
175     pen = wxPen(color, stroke_width, wxSOLID)
176 bh 6
177 bh 1419 draw_func(draw_func_param, i, pen, brush)
178 jonathan 798
179 jonathan 938 def draw_raster_layer(self, layer):
180     data = None
181     offx, offy = self.offset
182     width, height = self.dc.GetSizeTuple()
183    
184     inProj = ""
185     proj = layer.GetProjection()
186     if proj is not None:
187     for p in proj.GetAllParameters():
188     inProj += "+" + p + " "
189    
190     outProj = ""
191     proj = self.map.GetProjection()
192     if proj is not None:
193     for p in proj.GetAllParameters():
194     outProj += "+" + p + " "
195    
196     xmin = (0 - offx) / self.scale
197     ymin = (offy - height) / self.scale
198     xmax = (width - offx) / self.scale
199     ymax = (offy - 0) / self.scale
200    
201     try:
202     data = ProjectRasterFile(
203     layer.GetImageFilename(),
204     inProj,
205     outProj,
206 jonathan 1103 (xmin, ymin, xmax, ymax),
207     "", (width, height))
208 jonathan 965 except IOError, (strerr):
209     print strerr
210     except (AttributeError, ValueError):
211 jonathan 938 pass
212     else:
213     if data is not None:
214     stream = cStringIO.StringIO(data)
215     image = wxImageFromStream(stream, wxBITMAP_TYPE_BMP)
216     bitmap = wxBitmapFromImage(image)
217     self.dc.BeginDrawing()
218     self.dc.DrawBitmap(bitmap, 0, 0)
219     self.dc.EndDrawing()
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 6 self.dc.SetBrush(brush)
356     self.dc.SetPen(pen)
357 bh 535 func = self.draw_point_shape
358     args = ()
359     else:
360     raise TypeError(_("Unhandled shape type %s") % shapetype)
361 bh 6
362 bh 535 for index in self.selected_shapes:
363 bh 686 func(renderparam, index, *args)
364 bh 535
365 bh 686
366 bh 144 def layer_ids(self, layer):
367     """Return the shapeids covered by the region that has to be redrawn
368 bh 6
369 bh 144 Call the layer's ShapesInRegion method to determine the ids so
370     that it can use the quadtree.
371     """
372     # FIXME: the quad-tree should be built from the projected
373     # coordinates not the lat-long ones because it's not trivial to
374     # determine an appropriate rectangle in lat-long for a given
375     # rectangle in projected coordinates which we have to start from
376     # here.
377     proj = self.map.projection
378     if proj is not None:
379     inverse = proj.Inverse
380     else:
381     inverse = None
382    
383     scale = self.scale
384     offx, offy = self.offset
385     xs = []
386     ys = []
387 bh 148 x, y, width, height = self.update_region
388 bh 144 for winx, winy in ((x, y), (x + width, y),
389     (x + width, y + height), (x, y + height)):
390     px = (winx - offx) / scale
391     py = -(winy - offy) / scale
392     if inverse:
393     px, py = inverse(px, py)
394     xs.append(px)
395     ys.append(py)
396     left = min(xs)
397     right = max(xs)
398     top = max(ys)
399     bottom = min(ys)
400    
401     return layer.ShapesInRegion((left, bottom, right, top))
402    
403    
404 frank 909 class ExportRenderer(ScreenRenderer):
405 bh 6
406 frank 909 honor_visibility = 1
407    
408     def RenderMap(self, map, region, mapregion,
409     selected_layer, selected_shapes ):
410     """Render the map.
411 bh 6
412 frank 909 The rendering device has been specified during initialisation.
413     The device border distance was set in Thuban.UI.view.OutputTranform().
414    
415     RenderMap renders a frame set (one page frame, one around
416     legend/scalebar and one around the map), the map, the legend and the
417     scalebar on the given DC. The map is rendered with the region displayed
418     in the canvas view, centered on the area available for map display.
419     """
420    
421     self.update_region = region
422     self.selected_layer = selected_layer
423     self.selected_shapes = selected_shapes
424    
425     # Get some dimensions
426     llx, lly, urx, ury = region
427     self.mapregion = mapregion
428     mminx, mminy, mmaxx, mmaxy = self.mapregion
429    
430     # Manipulate the offset to position the map
431     offx, offy = self.offset
432     # 1. Shift to corner of map drawing area
433     offx = offx + mminx
434     offy = offy + mminy
435    
436     # 2. Center the map on the map drawing area:
437     # region identifies the region on the canvas view:
438     # center of map drawing area - half the size of region: rendering origin
439     self.shiftx = (mmaxx - mminx)*0.5 - (urx - llx)*0.5
440     self.shifty = (mmaxy - mminy)*0.5 - (ury - lly)*0.5
441    
442     self.offset = (offx+self.shiftx, offy+self.shifty)
443    
444     # Draw the map
445     self.dc.BeginDrawing()
446     self.dc.DestroyClippingRegion()
447     self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
448     urx, ury)
449     self.render_map(map)
450     self.dc.EndDrawing()
451    
452     # Draw the rest (frames, legend, scalebar)
453     self.dc.BeginDrawing()
454     self.dc.DestroyClippingRegion()
455    
456     # Force the font for Legend drawing
457     font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
458     self.dc.SetFont(font)
459    
460     self.render_frame(region)
461     self.render_legend(map)
462     self.render_scalebar(map)
463     self.dc.EndDrawing()
464    
465     def render_frame(self, region):
466     """Render the frames for map and legend/scalebar."""
467    
468     dc = self.dc
469     dc.SetPen(wxBLACK_PEN)
470     dc.SetBrush(wxTRANSPARENT_BRUSH)
471    
472     # Dimension stuff
473     width, height = dc.GetSizeTuple()
474     mminx, mminy, mmaxx, mmaxy = self.mapregion
475    
476     # Page Frame
477     dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
478    
479     # Map Frame
480     llx, lly, urx, ury = region
481     dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
482    
483     # Legend Frame
484     dc.DrawRectangle(mmaxx+10,mminy,(width-20) - (mmaxx+10), mmaxy-mminy)
485    
486     dc.DestroyClippingRegion()
487     dc.SetClippingRegion(mmaxx+10,mminy,
488     (width-20) - (mmaxx+10), mmaxy-mminy)
489    
490     def render_legend(self, map):
491     """Render the legend on the Map."""
492    
493     previewer = ClassDataPreviewer()
494     dc = self.dc
495     dc.SetPen(wxBLACK_PEN)
496     dc.SetBrush(wxTRANSPARENT_BRUSH)
497    
498     # Dimension stuff
499     width, height = dc.GetSizeTuple()
500     mminx, mminy, mmaxx, mmaxy = self.mapregion
501     textwidth, textheight = dc.GetTextExtent("0")
502     iconwidth = textheight
503     iconheight = textheight
504     stepy = textheight+3
505     dx = 10
506     posx = mmaxx + 10 + 5 # 10 pix distance mapframe/legend frame,
507     # 5 pix inside legend frame
508     posy = mminy + 5 # 5 pix inside legend frame
509    
510     # Render the legend
511     dc.SetTextForeground(wxBLACK)
512     if map.HasLayers():
513 frank 1324 layers = map.Layers()
514     layers.reverse()
515     for l in layers:
516 frank 909 if l.Visible():
517     # Render title
518     dc.DrawText(l.Title(), posx, posy)
519     posy+=stepy
520 jonathan 1298 if l.HasClassification():
521     # Render classification
522     clazz = l.GetClassification()
523     shapeType = l.ShapeType()
524     for g in clazz:
525     if g.IsVisible():
526     previewer.Draw(dc,
527     wxRect(posx+dx, posy,
528     iconwidth, iconheight),
529     g.GetProperties(), shapeType)
530     dc.DrawText(g.GetDisplayText(),
531     posx+2*dx+iconwidth, posy)
532     posy+=stepy
533 frank 909
534     def render_scalebar(self, map):
535     """Render the scalebar."""
536    
537     scalebar = ScaleBar(map)
538    
539     # Dimension stuff
540     width, height = self.dc.GetSizeTuple()
541     mminx, mminy, mmaxx, mmaxy = self.mapregion
542    
543     # Render the scalebar
544     scalebar.DrawScaleBar(self.scale, self.dc,
545     (mmaxx+10+5, mmaxy-25),
546     ((width-15-5) - (mmaxx+10+5),20)
547     )
548     # 10 pix between map and legend frame, 5 pix inside legend frame
549     # 25 pix from the legend frame bottom line
550     # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
551     # Height: 20
552    
553     class PrinterRenderer(ExportRenderer):
554    
555     # Printing as well as Export / Screen display only the visible layer.
556     honor_visibility = 1
557    

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26