/[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 798 by jonathan, Wed Apr 30 17:02:04 2003 UTC revision 1552 by bh, Wed Aug 6 17:21:32 2003 UTC
# Line 2  Line 2 
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4  # Jonathan Coles <[email protected]>  # 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, wxPen, wxBrush, wxFont, \  import cStringIO
      wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \  
      wxBLACK, wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL  
   
 from wxproj import draw_polygon_shape, draw_polygon_init  
13    
14  from Thuban import _  from Thuban import _
 from Thuban.UI.common import *  
   
 from Thuban.Model.layer import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \  
      SHAPETYPE_POINT  
 from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \  
      ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE  
   
 from Thuban.Model.classification import Classification  
 from Thuban.Model.color import Color  
   
 class MapRenderer:  
15    
16      """Class to render a map onto a wxDC"""  from wxPython.wx import wxPoint, wxRect, wxPen, wxBrush, wxFont, \
17        wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
18      honor_visibility = 1      wxBLACK_PEN, wxBLACK, wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
19        wxBitmapFromImage, wxImageFromStream, wxBITMAP_TYPE_BMP
20    
21      def __init__(self, dc, scale, offset, resolution = 72.0,  from wxproj import draw_polygon_shape, draw_polygon_init
                  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  
22    
23          self.dc = dc  from Thuban.UI.common import Color2wxColour
24          self.scale = scale  from Thuban.UI.classifier import ClassDataPreviewer
25          self.offset = offset  from Thuban.UI.scalebar import ScaleBar
         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())  
26    
27      def draw_shape_layer(self, layer):  from Thuban.Model.data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
         scale = self.scale  
         offx, offy = self.offset  
28    
29          map_proj = self.map.projection  from Thuban.Model.color import Transparent
30          layer_proj = layer.projection  import Thuban.Model.resource
31    
32          shapetype = layer.ShapeType()  from baserenderer import BaseRenderer
33    
34          brush = wxTRANSPARENT_BRUSH  class MapRenderer(BaseRenderer):
         pen   = wxTRANSPARENT_PEN  
35    
36          old_prop = None      """Class to render a map onto a wxDC"""
         old_group = None  
         lc = layer.GetClassification()  
         field = lc.GetField()  
         defaultGroup = lc.GetDefaultGroup()  
37    
38        TRANSPARENT_PEN = wxTRANSPARENT_PEN
39        TRANSPARENT_BRUSH = wxTRANSPARENT_BRUSH
40    
41          if shapetype != SHAPETYPE_POINT:      make_point = wxPoint
             polygon_render_param = self.polygon_render_param(layer)  
42    
43          if shapetype == SHAPETYPE_POINT:      def tools_for_property(self, prop):
44              draw_func = lambda i: \          fill = prop.GetFill()
45                     self.draw_point_shape(layer, i)          if fill is Transparent:
46                brush = self.TRANSPARENT_BRUSH
47          else:          else:
48              draw_func = lambda i: \              brush = wxBrush(Color2wxColour(fill), wxSOLID)
                    self.draw_polygon_shape(polygon_render_param, i, pen, brush)  
               
         for i in self.layer_ids(layer):  
   
             if field is None:  
                 group = defaultGroup  
             else:  
                 record = layer.table.read_record(i)  
                 assert record is not None  
                 group = lc.FindGroup(record[field])  
   
   
             if not group.IsVisible():  
                 continue  
   
49    
50              # don't recreate new objects if they are the same as before          stroke = prop.GetLineColor()
51              if group is not old_group:          if stroke is Transparent:
52                  old_group = group              pen = self.TRANSPARENT_PEN
   
                 prop = group.GetProperties()  
   
                 if prop != old_prop:  
                     old_prop = prop  
   
                     if shapetype == SHAPETYPE_ARC:  
                         fill = Color.Transparent  
                     else:  
                         fill = prop.GetFill()  
   
   
                     if fill is Color.Transparent:  
                         brush = wxTRANSPARENT_BRUSH  
                     else:  
                         color = Color2wxColour(fill)  
                         brush = wxBrush(color, wxSOLID)  
   
                     stroke = prop.GetLineColor()  
                     stroke_width = prop.GetLineWidth()  
                     if stroke is Color.Transparent:  
                         pen = wxTRANSPARENT_PEN  
                     else:  
                         color = Color2wxColour(stroke)  
                         pen = wxPen(color, stroke_width, wxSOLID)  
   
                     if shapetype == SHAPETYPE_POINT:  
                         self.dc.SetBrush(brush)  
                         self.dc.SetPen(pen)  
   
             draw_func(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 polygon_render_param(self, layer):  
         """Return the low-lever render parameter for the layer"""  
         offx, offy = self.offset  
         return draw_polygon_init(layer.shapefile, self.dc,  
                                  self.map.projection,  
                                  layer.projection,  
                                  self.scale, -self.scale,  
                                  offx, offy)  
   
     def draw_polygon_shape(self, draw_polygon_info, index, pen, brush):  
         draw_polygon_shape(draw_polygon_info, index, pen, brush)  
   
     def projected_points(self, layer, index):  
         proj = self.map.GetProjection()  
         if proj is not None:  
             forward = proj.Forward  
         else:  
             forward = None  
         proj = layer.GetProjection()  
         if proj is not None:  
             inverse = proj.Inverse  
53          else:          else:
54              inverse = None              pen = wxPen(Color2wxColour(stroke), prop.GetLineWidth(), wxSOLID)
55          shape = layer.Shape(index)          return pen, brush
         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):  
         pp = self.projected_points(layer, index)  
   
         if len(pp) == 0: return # ignore Null Shapes which have no points  
   
         p = pp[0]  
         radius = self.resolution * 5  
         self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)  
56    
57      def draw_label_layer(self, layer):      def low_level_renderer(self, layer):
58          scale = self.scale          """Override inherited method to provide more efficient renderers
         offx, offy = self.offset  
   
         font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)  
         self.dc.SetFont(font)  
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_shapes):      def RenderMap(self, map, region, selected_layer, selected_shapes):
95          """Render the map.          """Render the map.
96    
# Line 265  class ScreenRenderer(MapRenderer): Line 111  class ScreenRenderer(MapRenderer):
111              brush = wxBrush(wxBLACK, wxCROSS_HATCH)              brush = wxBrush(wxBLACK, wxCROSS_HATCH)
112    
113              shapetype = layer.ShapeType()              shapetype = layer.ShapeType()
114              if shapetype == SHAPETYPE_POLYGON:              func, param = self.low_level_renderer(layer)
115                  offx, offy = self.offset              args = (pen, brush)
                 renderparam = self.polygon_render_param(layer)  
                 func = self.draw_polygon_shape  
                 args = (pen, brush)  
             elif shapetype == SHAPETYPE_ARC:  
                 renderparam = self.polygon_render_param(layer)  
                 func = self.draw_polygon_shape  
                 args = (pen, None)  
             elif shapetype == SHAPETYPE_POINT:  
                 renderparam = layer  
                 self.dc.SetBrush(brush)  
                 self.dc.SetPen(pen)  
                 func = self.draw_point_shape  
                 args = ()  
             else:  
                 raise TypeError(_("Unhandled shape type %s") % shapetype)  
   
116              for index in self.selected_shapes:              for index in self.selected_shapes:
117                  func(renderparam, index, *args)                  func(param, index, *args)
   
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 325  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      # When printing we want to see all layers      honor_visibility = 1
160      honor_visibility = 0  
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            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            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.798  
changed lines
  Added in v.1552

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26