/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/renderer.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/UI/renderer.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 290 by bh, Thu Aug 29 14:11:28 2002 UTC revision 1552 by bh, Wed Aug 6 17:21:32 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002 by Intevation GmbH  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4    # Jonathan Coles <[email protected]>
5    # Frank Koormann <[email protected]>
6  #  #
7  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
8  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
9    
10  __version__ = "$Revision$"  __version__ = "$Revision$"
11    
12  from wxPython.wx import wxPoint, wxColour, wxPen, wxBrush, wxFont, \  import cStringIO
      wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \  
      wxBLACK, wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL  
13    
14  from wxproj import draw_polygon_shape  from Thuban import _
15    
16  from Thuban.Model.layer import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \  from wxPython.wx import wxPoint, wxRect, wxPen, wxBrush, wxFont, \
17       SHAPETYPE_POINT      wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
18  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \      wxBLACK_PEN, wxBLACK, wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
19       ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE      wxBitmapFromImage, wxImageFromStream, wxBITMAP_TYPE_BMP
20    
21    from wxproj import draw_polygon_shape, draw_polygon_init
22    
23  class MapRenderer:  from Thuban.UI.common import Color2wxColour
24    from Thuban.UI.classifier import ClassDataPreviewer
25    from Thuban.UI.scalebar import ScaleBar
26    
27      """Class to render a map onto a wxDC"""  from Thuban.Model.data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
28    
29      honor_visibility = 1  from Thuban.Model.color import Transparent
30    import Thuban.Model.resource
31    
32      def __init__(self, dc, scale, offset, resolution = 72.0,  from baserenderer import BaseRenderer
                  honor_visibility = None):  
         """Inititalize the renderer.  
   
         dc -- the wxPython DC to render on  
         scale, offset -- the scale factor and translation to convert  
                 between projected coordinates and the DC coordinates  
   
         resolution -- the assumed resolution of the DC. Used to convert  
                 absolute lengths like font sizes to DC coordinates  
   
         honor_visibility -- boolean. If true, honor the visibility flag  
                 of the layers, otherwise draw all layers. If None, use  
                 the renderer's default.  
         """  
         # resolution in pixel/inch  
         self.dc = dc  
         self.scale = scale  
         self.offset = offset  
         if honor_visibility is not None:  
             self.honor_visibility = honor_visibility  
         # store the resolution in pixel/point because it's more useful  
         # later.  
         self.resolution = resolution / 72.0  
   
     def render_map(self, map):  
         self.map = map  
         for layer in map.Layers():  
             # if honor_visibility is true, only draw visible layers,  
             # otherwise draw all layers  
             if not self.honor_visibility or layer.Visible():  
                 self.draw_shape_layer(layer)  
         self.draw_label_layer(map.LabelLayer())  
33    
34      def draw_shape_layer(self, layer):  class MapRenderer(BaseRenderer):
         scale = self.scale  
         offx, offy = self.offset  
35    
36          fill = layer.fill      """Class to render a map onto a wxDC"""
         if fill is None:  
             brush = wxTRANSPARENT_BRUSH  
         else:  
             color = wxColour(fill.red * 255,  
                              fill.green * 255,  
                              fill.blue * 255)  
             brush = wxBrush(color, wxSOLID)  
         stroke = layer.stroke  
         stroke_width = layer.stroke_width  
         if stroke is None:  
             pen = wxTRANSPARENT_PEN  
         else:  
             color = wxColour(stroke.red * 255,  
                              stroke.green * 255,  
                              stroke.blue * 255)  
             pen = wxPen(color, stroke_width, wxSOLID)  
37    
38          map_proj = self.map.projection      TRANSPARENT_PEN = wxTRANSPARENT_PEN
39          layer_proj = layer.projection      TRANSPARENT_BRUSH = wxTRANSPARENT_BRUSH
40    
41          shapetype = layer.ShapeType()      make_point = wxPoint
42    
43          if shapetype == SHAPETYPE_POLYGON:      def tools_for_property(self, prop):
44              for i in self.layer_ids(layer):          fill = prop.GetFill()
45                  self.draw_polygon_shape(layer, i, pen, brush)          if fill is Transparent:
46          elif shapetype == SHAPETYPE_ARC:              brush = self.TRANSPARENT_BRUSH
             for i in self.layer_ids(layer):  
                 self.draw_polygon_shape(layer, i, pen, None)  
47          else:          else:
48              self.dc.SetBrush(brush)              brush = wxBrush(Color2wxColour(fill), wxSOLID)
             self.dc.SetPen(pen)  
             if shapetype == SHAPETYPE_ARC:  
                 f = self.draw_arc_shape  
             elif shapetype == SHAPETYPE_POINT:  
                 f = self.draw_point_shape  
             for i in self.layer_ids(layer):  
                 f(layer, i)  
   
     def layer_ids(self, layer):  
         """Return the shape ids of the given layer that have to be drawn.  
           
         The default implementation simply returns all ids in the layer.  
         Override in derived classes to be more precise.  
         """  
         return range(layer.NumShapes())  
   
     def draw_polygon_shape(self, layer, index, pen, brush):  
         offx, offy = self.offset          
         draw_polygon_shape(layer.shapefile.cobject(), index,  
                            self.dc, pen, brush,  
                            self.map.projection, layer.projection,  
                            self.scale, -self.scale, offx, offy)  
49    
50      def projected_points(self, layer, index):          stroke = prop.GetLineColor()
51          proj = self.map.projection          if stroke is Transparent:
52          if proj is not None:              pen = self.TRANSPARENT_PEN
             forward = proj.Forward  
53          else:          else:
54              forward = None              pen = wxPen(Color2wxColour(stroke), prop.GetLineWidth(), wxSOLID)
55          proj = layer.projection          return pen, brush
         if proj is not None:  
             inverse = proj.Inverse  
         else:  
             inverse = None  
         shape = layer.Shape(index)  
         points = []  
         scale = self.scale  
         offx, offy = self.offset  
         for x, y in shape.Points():  
             if inverse:  
                 x, y = inverse(x, y)  
             if forward:  
                 x, y = forward(x, y)  
             points.append(wxPoint(x * scale + offx,  
                                   -y * scale + offy))  
         return points  
   
     def draw_arc_shape(self, layer, index):  
         points = self.projected_points(layer, index)  
         self.dc.DrawLines(points)  
   
     def draw_point_shape(self, layer, index):  
         p = self.projected_points(layer, index)[0]  
         radius = self.resolution * 5  
         self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)  
   
     def draw_label_layer(self, layer):  
         scale = self.scale  
         offx, offy = self.offset  
56    
57          font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)      def low_level_renderer(self, layer):
58          self.dc.SetFont(font)          """Override inherited method to provide more efficient renderers
59    
60          map_proj = self.map.projection          For point shapes, return self.draw_point_shape and layer just as
61          if map_proj is not None:          the base class method. For arc and polygon use the more
62              forward = map_proj.Forward          efficient wxproj.draw_polygon_shape and its corresponding
63            parameter created with wxproj.draw_polygon_init.
64            """
65            shapetype = layer.ShapeType()
66            if shapetype == SHAPETYPE_POINT:
67                func = self.draw_point_shape
68                param = layer
69          else:          else:
70              forward = None              offx, offy = self.offset
71                param = draw_polygon_init(layer.ShapeStore().Shapefile(), self.dc,
72          for label in layer.Labels():                                        self.map.projection,
73              x = label.x                                        layer.projection,
74              y = label.y                                        self.scale, -self.scale,
75              text = label.text                                        offx, offy)
76              if forward:              func = draw_polygon_shape
77                  x, y = forward(x, y)          return func, param
78              x = x * scale + offx  
79              y = -y * scale + offy      def label_font(self):
80              width, height = self.dc.GetTextExtent(text)          return wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
81              if label.halign == ALIGN_LEFT:  
82                  # nothing to be done      def draw_raster_data(self, data):
83                  pass          stream = cStringIO.StringIO(data)
84              elif label.halign == ALIGN_RIGHT:          image = wxImageFromStream(stream, wxBITMAP_TYPE_BMP)
85                  x = x - width          bitmap = wxBitmapFromImage(image)
86              elif label.halign == ALIGN_CENTER:          self.dc.DrawBitmap(bitmap, 0, 0)
                 x = x - width/2  
             if label.valign == ALIGN_TOP:  
                 # nothing to be done  
                 pass  
             elif label.valign == ALIGN_BOTTOM:  
                 y = y - height  
             elif label.valign == ALIGN_CENTER:  
                 y = y - height/2  
             self.dc.DrawText(text, x, y)  
87    
88    
89  class ScreenRenderer(MapRenderer):  class ScreenRenderer(MapRenderer):
90    
91      # On the screen we want to see only visible layers by default      # On the screen we want to see only visible layers by default
92      honor_visibility = 1      honor_visibility = 1
93        
94      def RenderMap(self, map, region, selected_layer, selected_shape):      def RenderMap(self, map, region, selected_layer, selected_shapes):
95          """Render the map.          """Render the map.
96    
97          Only the given region (a tuple in window coordinates as returned          Only the given region (a tuple in window coordinates as returned
98          by a wxrect's asTuple method) needs to be redrawn. Highlight the          by a wxrect's asTuple method) needs to be redrawn. Highlight the
99          shape with id selected_shape in the selected_layer.          shapes given by the ids in selected_shapes in the
100            selected_layer.
101          """          """
102          self.update_region = region          self.update_region = region
103          self.selected_layer = selected_layer          self.selected_layer = selected_layer
104          self.selected_shape = selected_shape          self.selected_shapes = selected_shapes
105          self.render_map(map)          self.render_map(map)
106    
107      def draw_shape_layer(self, layer):      def draw_shape_layer(self, layer):
108          MapRenderer.draw_shape_layer(self, layer)          MapRenderer.draw_shape_layer(self, layer)
109          if layer is self.selected_layer and self.selected_shape is not None:          if layer is self.selected_layer and self.selected_shapes:
110              pen = wxPen(wxBLACK, 3, wxSOLID)              pen = wxPen(wxBLACK, 3, wxSOLID)
111              brush = wxBrush(wxBLACK, wxCROSS_HATCH)              brush = wxBrush(wxBLACK, wxCROSS_HATCH)
112                
113              shapetype = layer.ShapeType()              shapetype = layer.ShapeType()
114              index = self.selected_shape              func, param = self.low_level_renderer(layer)
115              if shapetype == SHAPETYPE_POLYGON:              args = (pen, brush)
116                  self.draw_polygon_shape(layer, index, pen, brush)              for index in self.selected_shapes:
117              elif shapetype == SHAPETYPE_ARC:                  func(param, index, *args)
                 self.draw_polygon_shape(layer, index, pen, None)  
             else:  
                 self.dc.SetBrush(brush)  
                 self.dc.SetPen(pen)  
                 if shapetype == SHAPETYPE_POINT:  
                     self.draw_point_shape(layer, index)  
                 else:  
                     raise TypeError("Unhandled shape type %s" % shapetype)  
118    
119      def layer_ids(self, layer):      def layer_ids(self, layer):
120          """Return the shapeids covered by the region that has to be redrawn          """Return the shapeids covered by the region that has to be redrawn
# Line 264  class ScreenRenderer(MapRenderer): Line 154  class ScreenRenderer(MapRenderer):
154          return layer.ShapesInRegion((left, bottom, right, top))          return layer.ShapesInRegion((left, bottom, right, top))
155    
156    
157  class PrinterRender(MapRenderer):  class ExportRenderer(ScreenRenderer):
158    
159        honor_visibility = 1
160    
161        def RenderMap(self, map, region, mapregion,
162                      selected_layer, selected_shapes ):
163            """Render the map.
164    
165            The rendering device has been specified during initialisation.
166            The device border distance was set in Thuban.UI.view.OutputTranform().
167    
168      # When printing we want to see all layers          RenderMap renders a frame set (one page frame, one around
169      honor_visibility = 0          legend/scalebar and one around the map), the map, the legend and the
170            scalebar on the given DC. The map is rendered with the region displayed
171            in the canvas view, centered on the area available for map display.
172            """
173    
174            self.update_region = region
175            self.selected_layer = selected_layer
176            self.selected_shapes = selected_shapes
177    
178            # Get some dimensions
179            llx, lly, urx, ury = region
180            self.mapregion = mapregion
181            mminx, mminy, mmaxx, mmaxy = self.mapregion
182    
183            # Manipulate the offset to position the map
184            offx, offy = self.offset
185            # 1. Shift to corner of map drawing area
186            offx = offx + mminx
187            offy = offy + mminy
188    
189            # 2. Center the map on the map drawing area:
190            # region identifies the region on the canvas view:
191            # center of map drawing area - half the size of region: rendering origin
192            self.shiftx = (mmaxx - mminx)*0.5 - (urx - llx)*0.5
193            self.shifty = (mmaxy - mminy)*0.5 - (ury - lly)*0.5
194    
195            self.offset = (offx+self.shiftx, offy+self.shifty)
196    
197            # Draw the map
198            self.dc.BeginDrawing()
199            self.dc.DestroyClippingRegion()
200            self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
201                                      urx, ury)
202            self.render_map(map)
203            self.dc.EndDrawing()
204    
205            # Draw the rest (frames, legend, scalebar)
206            self.dc.BeginDrawing()
207            self.dc.DestroyClippingRegion()
208    
209            # Force the font for Legend drawing
210            font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
211            self.dc.SetFont(font)
212    
213            self.render_frame(region)
214            self.render_legend(map)
215            self.render_scalebar(map)
216            self.dc.EndDrawing()
217    
218        def render_frame(self, region):
219            """Render the frames for map and legend/scalebar."""
220    
221            dc = self.dc
222            dc.SetPen(wxBLACK_PEN)
223            dc.SetBrush(wxTRANSPARENT_BRUSH)
224    
225            # Dimension stuff
226            width, height = dc.GetSizeTuple()
227            mminx, mminy, mmaxx, mmaxy = self.mapregion
228    
229            # Page Frame
230            dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
231    
232            # Map Frame
233            llx, lly, urx, ury = region
234            dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
235    
236            # Legend Frame
237            dc.DrawRectangle(mmaxx+10,mminy,(width-20) - (mmaxx+10), mmaxy-mminy)
238    
239            dc.DestroyClippingRegion()
240            dc.SetClippingRegion(mmaxx+10,mminy,
241                                 (width-20) - (mmaxx+10), mmaxy-mminy)
242    
243        def render_legend(self, map):
244            """Render the legend on the Map."""
245    
246            previewer = ClassDataPreviewer()
247            dc = self.dc
248            dc.SetPen(wxBLACK_PEN)
249            dc.SetBrush(wxTRANSPARENT_BRUSH)
250    
251            # Dimension stuff
252            width, height = dc.GetSizeTuple()
253            mminx, mminy, mmaxx, mmaxy = self.mapregion
254            textwidth, textheight = dc.GetTextExtent("0")
255            iconwidth  = textheight
256            iconheight = textheight
257            stepy = textheight+3
258            dx = 10
259            posx = mmaxx + 10 + 5   # 10 pix distance mapframe/legend frame,
260                                    # 5 pix inside legend frame
261            posy = mminy + 5        # 5 pix inside legend frame
262    
263            # Render the legend
264            dc.SetTextForeground(wxBLACK)
265            if map.HasLayers():
266                layers = map.Layers()
267                layers.reverse()
268                for l in layers:
269                    if l.Visible():
270                        # Render title
271                        dc.DrawText(l.Title(), posx, posy)
272                        posy+=stepy
273                        if l.HasClassification():
274                            # Render classification
275                            clazz = l.GetClassification()
276                            shapeType = l.ShapeType()
277                            for g in clazz:
278                                if g.IsVisible():
279                                    previewer.Draw(dc,
280                                        wxRect(posx+dx, posy,
281                                               iconwidth, iconheight),
282                                        g.GetProperties(), shapeType)
283                                    dc.DrawText(g.GetDisplayText(),
284                                                posx+2*dx+iconwidth, posy)
285                                    posy+=stepy
286    
287        def render_scalebar(self, map):
288            """Render the scalebar."""
289    
290            scalebar = ScaleBar(map)
291    
292            # Dimension stuff
293            width, height = self.dc.GetSizeTuple()
294            mminx, mminy, mmaxx, mmaxy = self.mapregion
295    
296            # Render the scalebar
297            scalebar.DrawScaleBar(self.scale, self.dc,
298                                 (mmaxx+10+5, mmaxy-25),
299                                 ((width-15-5) - (mmaxx+10+5),20)
300                                )
301            # 10 pix between map and legend frame, 5 pix inside legend frame
302            # 25 pix from the legend frame bottom line
303            # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
304            # Height: 20
305    
306    class PrinterRenderer(ExportRenderer):
307    
308        # Printing as well as Export / Screen display only the visible layer.
309        honor_visibility = 1
310    
     RenderMap = MapRenderer.render_map  
       

Legend:
Removed from v.290  
changed lines
  Added in v.1552

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26