/[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 76 by bh, Mon Feb 4 19:23:30 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):
35          scale = self.scale  
36          offx, offy = self.offset      """Class to render a map onto a wxDC"""
37    
38          fill = layer.fill      TRANSPARENT_PEN = wxTRANSPARENT_PEN
39          if fill is None:      TRANSPARENT_BRUSH = wxTRANSPARENT_BRUSH
40              brush = wxTRANSPARENT_BRUSH  
41        make_point = wxPoint
42    
43        def tools_for_property(self, prop):
44            fill = prop.GetFill()
45            if fill is Transparent:
46                brush = self.TRANSPARENT_BRUSH
47          else:          else:
48              color = wxColour(fill.red * 255,              brush = wxBrush(Color2wxColour(fill), wxSOLID)
49                               fill.green * 255,  
50                               fill.blue * 255)          stroke = prop.GetLineColor()
51              brush = wxBrush(color, wxSOLID)          if stroke is Transparent:
52          stroke = layer.stroke              pen = self.TRANSPARENT_PEN
         stroke_width = layer.stroke_width  
         if stroke is None:  
             pen = wxTRANSPARENT_PEN  
53          else:          else:
54              color = wxColour(stroke.red * 255,              pen = wxPen(Color2wxColour(stroke), prop.GetLineWidth(), wxSOLID)
55                               stroke.green * 255,          return pen, brush
                              stroke.blue * 255)  
             pen = wxPen(color, stroke_width, wxSOLID)  
56    
57          map_proj = self.map.projection      def low_level_renderer(self, layer):
58          layer_proj = layer.projection          """Override inherited method to provide more efficient renderers
59    
60            For point shapes, return self.draw_point_shape and layer just as
61            the base class method. For arc and polygon use the more
62            efficient wxproj.draw_polygon_shape and its corresponding
63            parameter created with wxproj.draw_polygon_init.
64            """
65          shapetype = layer.ShapeType()          shapetype = layer.ShapeType()
66            if shapetype == SHAPETYPE_POINT:
67          if shapetype == SHAPETYPE_POLYGON:              func = self.draw_point_shape
68              for i in range(layer.NumShapes()):              param = layer
                 self.draw_polygon_shape(layer, i, pen, brush)  
69          else:          else:
70              self.dc.SetBrush(brush)              offx, offy = self.offset
71              self.dc.SetPen(pen)              param = draw_polygon_init(layer.ShapeStore().Shapefile(), self.dc,
72              if shapetype == SHAPETYPE_ARC:                                        self.map.projection,
73                  f = self.draw_arc_shape                                        layer.projection,
74              elif shapetype == SHAPETYPE_POINT:                                        self.scale, -self.scale,
75                  f = self.draw_point_shape                                        offx, offy)
76              for i in range(layer.NumShapes()):              func = draw_polygon_shape
77                  f(layer, i)          return func, param
78    
79      def draw_polygon_shape(self, layer, index, pen, brush):      def label_font(self):
80          offx, offy = self.offset                  return wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
81          draw_polygon_shape(layer.shapefile.cobject(), index,  
82                             self.dc, pen, brush,      def draw_raster_data(self, data):
83                             self.map.projection, layer.projection,          stream = cStringIO.StringIO(data)
84                             self.scale, -self.scale, offx, offy)          image = wxImageFromStream(stream, wxBITMAP_TYPE_BMP)
85            bitmap = wxBitmapFromImage(image)
86            self.dc.DrawBitmap(bitmap, 0, 0)
87    
88    
89    class ScreenRenderer(MapRenderer):
90    
91        # On the screen we want to see only visible layers by default
92        honor_visibility = 1
93    
94      def projected_points(self, layer, index):      def RenderMap(self, map, region, selected_layer, selected_shapes):
95            """Render the map.
96    
97            Only the given region (a tuple in window coordinates as returned
98            by a wxrect's asTuple method) needs to be redrawn. Highlight the
99            shapes given by the ids in selected_shapes in the
100            selected_layer.
101            """
102            self.update_region = region
103            self.selected_layer = selected_layer
104            self.selected_shapes = selected_shapes
105            self.render_map(map)
106    
107        def draw_shape_layer(self, layer):
108            MapRenderer.draw_shape_layer(self, layer)
109            if layer is self.selected_layer and self.selected_shapes:
110                pen = wxPen(wxBLACK, 3, wxSOLID)
111                brush = wxBrush(wxBLACK, wxCROSS_HATCH)
112    
113                shapetype = layer.ShapeType()
114                func, param = self.low_level_renderer(layer)
115                args = (pen, brush)
116                for index in self.selected_shapes:
117                    func(param, index, *args)
118    
119        def layer_ids(self, layer):
120            """Return the shapeids covered by the region that has to be redrawn
121    
122            Call the layer's ShapesInRegion method to determine the ids so
123            that it can use the quadtree.
124            """
125            # FIXME: the quad-tree should be built from the projected
126            # coordinates not the lat-long ones because it's not trivial to
127            # determine an appropriate rectangle in lat-long for a given
128            # rectangle in projected coordinates which we have to start from
129            # here.
130          proj = self.map.projection          proj = self.map.projection
131          if proj is not None:          if proj is not None:
             forward = proj.Forward  
         else:  
             forward = None  
         proj = layer.projection  
         if proj is not None:  
132              inverse = proj.Inverse              inverse = proj.Inverse
133          else:          else:
134              inverse = None              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)  
135    
     def draw_label_layer(self, layer):  
136          scale = self.scale          scale = self.scale
137          offx, offy = self.offset          offx, offy = self.offset
138            xs = []
139            ys = []
140            x, y, width, height = self.update_region
141            for winx, winy in ((x, y), (x + width, y),
142                               (x + width, y + height), (x, y + height)):
143                px = (winx - offx) / scale
144                py = -(winy - offy) / scale
145                if inverse:
146                    px, py = inverse(px, py)
147                xs.append(px)
148                ys.append(py)
149            left = min(xs)
150            right = max(xs)
151            top = max(ys)
152            bottom = min(ys)
153    
154          font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)          return layer.ShapesInRegion((left, bottom, right, top))
         self.dc.SetFont(font)  
155    
         map_proj = self.map.projection  
         if map_proj is not None:  
             forward = map_proj.Forward  
         else:  
             forward = None  
156    
157          for label in layer.Labels():  class ExportRenderer(ScreenRenderer):
             x = label.x  
             y = label.y  
             text = label.text  
             if forward:  
                 x, y = forward(x, y)  
             x = x * scale + offx  
             y = -y * scale + offy  
             width, height = self.dc.GetTextExtent(text)  
             if label.halign == ALIGN_LEFT:  
                 # nothing to be done  
                 pass  
             elif label.halign == ALIGN_RIGHT:  
                 x = x - width  
             elif label.halign == ALIGN_CENTER:  
                 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)  
158    
159        honor_visibility = 1
160    
161  class ScreenRenderer(MapRenderer):      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            RenderMap renders a frame set (one page frame, one around
169            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      # On the screen we want to see only visible layers by default          self.update_region = region
     honor_visibility = 1  
       
     def RenderMap(self, map, selected_layer, selected_shape):  
175          self.selected_layer = selected_layer          self.selected_layer = selected_layer
176          self.selected_shape = selected_shape          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)          self.render_map(map)
203            self.dc.EndDrawing()
204    
205      def draw_shape_layer(self, layer):          # Draw the rest (frames, legend, scalebar)
206          MapRenderer.draw_shape_layer(self, layer)          self.dc.BeginDrawing()
207          if layer is self.selected_layer and self.selected_shape is not None:          self.dc.DestroyClippingRegion()
             pen = wxPen(wxBLACK, 3, wxSOLID)  
             brush = wxBrush(wxBLACK, wxCROSS_HATCH)  
               
             shapetype = layer.ShapeType()  
             index = self.selected_shape  
             if shapetype == SHAPETYPE_POLYGON:  
                 self.draw_polygon_shape(layer, index, pen, brush)  
             else:  
                 self.dc.SetBrush(brush)  
                 self.dc.SetPen(pen)  
                 if shapetype == SHAPETYPE_ARC:  
                     f = self.draw_arc_shape  
                 elif shapetype == SHAPETYPE_POINT:  
                     f = self.draw_point_shape  
                 f(layer, index)  
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 PrinterRender(MapRenderer):  class PrinterRenderer(ExportRenderer):
307    
308      # When printing we want to see all layers      # Printing as well as Export / Screen display only the visible layer.
309      honor_visibility = 0      honor_visibility = 1
310    
     RenderMap = MapRenderer.render_map  
       

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26