/[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 6 by bh, Tue Aug 28 15:41:52 2001 UTC revision 1419 by bh, Tue Jul 15 09:29:18 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001 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 wxMemoryDC, wxEmptyBitmap, \
17       SHAPETYPE_POINT      wxPoint, wxRect, wxPen, wxBrush, wxFont, \
18        wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
19        wxBLACK_PEN, wxRED_PEN, wxBLACK, \
20        wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
21        wxBitmap, wxImageFromBitmap, wxBitmapFromImage, \
22        wxImageFromStream, wxBITMAP_TYPE_BMP
23    
24    from wxproj import draw_polygon_shape, draw_polygon_init
25    
26    from Thuban.UI.common import Color2wxColour
27    from Thuban.UI.classifier import ClassDataPreviewer
28    from Thuban.UI.scalebar import ScaleBar
29    
30    from Thuban.Model.layer import Layer, RasterLayer, \
31         SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
32  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
33       ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE       ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE
34    
35    from Thuban.Model.classification import Classification
36    from Thuban.Model.color import Transparent
37    import Thuban.Model.resource
38    
39    if Thuban.Model.resource.has_gdal_support():
40        from gdalwarp import ProjectRasterFile
41    
42  class MapRenderer:  class MapRenderer:
43    
# Line 41  class MapRenderer: Line 61  class MapRenderer:
61                  the renderer's default.                  the renderer's default.
62          """          """
63          # resolution in pixel/inch          # resolution in pixel/inch
64    
65          self.dc = dc          self.dc = dc
66          self.scale = scale          self.scale = scale
67          self.offset = offset          self.offset = offset
# Line 52  class MapRenderer: Line 73  class MapRenderer:
73    
74      def render_map(self, map):      def render_map(self, map):
75          self.map = map          self.map = map
76            seenRaster = True
77    
78            if self.scale == 0:
79                return
80    
81            #
82            # This is only a good optimization if there is only one
83            # raster layer and the image covers the entire window (as
84            # 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            #
88            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    
94          for layer in map.Layers():          for layer in map.Layers():
95              # if honor_visibility is true, only draw visible layers,              # if honor_visibility is true, only draw visible layers,
96              # otherwise draw all layers              # otherwise draw all layers
97              if not self.honor_visibility or layer.Visible():              if not self.honor_visibility or layer.Visible():
98                  self.draw_shape_layer(layer)                  if isinstance(layer, Layer) and seenRaster:
99                        self.draw_shape_layer(layer)
100                    elif isinstance(layer, RasterLayer) \
101                        and Thuban.Model.resource.has_gdal_support():
102                        self.draw_raster_layer(layer)
103                        seenRaster = True
104    
105          self.draw_label_layer(map.LabelLayer())          self.draw_label_layer(map.LabelLayer())
106    
107      def draw_shape_layer(self, layer):      def draw_shape_layer(self, layer):
108          scale = self.scale          scale = self.scale
109          offx, offy = self.offset          offx, offy = self.offset
110    
         fill = layer.fill  
         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  
         if stroke is None:  
             pen = wxTRANSPARENT_PEN  
         else:  
             color = wxColour(stroke.red * 255,  
                              stroke.green * 255,  
                              stroke.blue * 255)  
             pen = wxPen(color, 1, wxSOLID)  
   
111          map_proj = self.map.projection          map_proj = self.map.projection
112          layer_proj = layer.projection          layer_proj = layer.projection
113    
114          shapetype = layer.ShapeType()          shapetype = layer.ShapeType()
115    
116          if shapetype == SHAPETYPE_POLYGON:          brush = wxTRANSPARENT_BRUSH
117              for i in range(layer.NumShapes()):          pen   = wxTRANSPARENT_PEN
118                  self.draw_polygon_shape(layer, i, pen, brush)  
119          else:          old_prop = None
120              self.dc.SetBrush(brush)          old_group = None
121              self.dc.SetPen(pen)          lc = layer.GetClassification()
122              if shapetype == SHAPETYPE_ARC:          field = lc.GetField()
123                  f = self.draw_arc_shape          defaultGroup = lc.GetDefaultGroup()
124              elif shapetype == SHAPETYPE_POINT:  
125                  f = self.draw_point_shape  
126              for i in range(layer.NumShapes()):          if shapetype == SHAPETYPE_POINT:
127                  f(layer, i)              draw_func = self.draw_point_shape
128                draw_func_param = layer
129      def draw_polygon_shape(self, layer, index, pen, brush):          else:
130          offx, offy = self.offset                      draw_func = draw_polygon_shape
131          draw_polygon_shape(layer.shapefile.cobject(), index,              draw_func_param = self.polygon_render_param(layer)
132                             self.dc, pen, brush,  
133                             self.map.projection, layer.projection,          table = layer.ShapeStore().Table()
134                             self.scale, -self.scale, offx, offy)          for i in self.layer_ids(layer):
135    
136                if field is None:
137                    group = defaultGroup
138                else:
139                    record = table.ReadRowAsDict(i)
140                    assert record is not None
141                    group = lc.FindGroup(record[field])
142    
143    
144                if not group.IsVisible():
145                    continue
146    
147    
148                # don't recreate new objects if they are the same as before
149                if group is not old_group:
150                    old_group = group
151    
152                    prop = group.GetProperties()
153    
154                    if prop != old_prop:
155                        old_prop = prop
156    
157                        if shapetype == SHAPETYPE_ARC:
158                            fill = Transparent
159                        else:
160                            fill = prop.GetFill()
161    
162    
163                        if fill is Transparent:
164                            brush = wxTRANSPARENT_BRUSH
165                        else:
166                            color = Color2wxColour(fill)
167                            brush = wxBrush(color, wxSOLID)
168    
169                        stroke = prop.GetLineColor()
170                        stroke_width = prop.GetLineWidth()
171                        if stroke is Transparent:
172                            pen = wxTRANSPARENT_PEN
173                        else:
174                            color = Color2wxColour(stroke)
175                            pen = wxPen(color, stroke_width, wxSOLID)
176    
177                draw_func(draw_func_param, i, pen, brush)
178    
179        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                    (xmin, ymin, xmax, ymax),
207                    "", (width, height))
208            except IOError, (strerr):
209                print strerr
210            except (AttributeError, ValueError):
211                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        def layer_ids(self, layer):
222            """Return the shape ids of the given layer that have to be drawn.
223    
224            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        def polygon_render_param(self, layer):
230            """Return the low-lever render parameter for the layer"""
231            offx, offy = self.offset
232            return draw_polygon_init(layer.ShapeStore().Shapefile(), self.dc,
233                                     self.map.projection,
234                                     layer.projection,
235                                     self.scale, -self.scale,
236                                     offx, offy)
237    
238        def draw_polygon_shape(self, draw_polygon_info, index, pen, brush):
239            draw_polygon_shape(draw_polygon_info, index, pen, brush)
240    
241      def projected_points(self, layer, index):      def projected_points(self, layer, index):
242          proj = self.map.projection          proj = self.map.GetProjection()
243          if proj is not None:          if proj is not None:
244              forward = proj.Forward              forward = proj.Forward
245          else:          else:
246              forward = None              forward = None
247          proj = layer.projection          proj = layer.GetProjection()
248          if proj is not None:          if proj is not None:
249              inverse = proj.Inverse              inverse = proj.Inverse
250          else:          else:
# Line 133  class MapRenderer: Line 266  class MapRenderer:
266          points = self.projected_points(layer, index)          points = self.projected_points(layer, index)
267          self.dc.DrawLines(points)          self.dc.DrawLines(points)
268    
269      def draw_point_shape(self, layer, index):      def draw_point_shape(self, layer, index, pen, brush):
270          p = self.projected_points(layer, index)[0]          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          radius = self.resolution * 5          radius = self.resolution * 5
276            self.dc.SetBrush(brush)
277            self.dc.SetPen(pen)
278          self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)          self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)
279    
280      def draw_label_layer(self, layer):      def draw_label_layer(self, layer):
# Line 182  class ScreenRenderer(MapRenderer): Line 321  class ScreenRenderer(MapRenderer):
321      # On the screen we want to see only visible layers by default      # On the screen we want to see only visible layers by default
322      honor_visibility = 1      honor_visibility = 1
323            
324      def RenderMap(self, map, selected_layer, selected_shape):      def RenderMap(self, map, region, selected_layer, selected_shapes):
325            """Render the map.
326    
327            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            shapes given by the ids in selected_shapes in the
330            selected_layer.
331            """
332            self.update_region = region
333          self.selected_layer = selected_layer          self.selected_layer = selected_layer
334          self.selected_shape = selected_shape          self.selected_shapes = selected_shapes
335          self.render_map(map)          self.render_map(map)
336    
337      def draw_shape_layer(self, layer):      def draw_shape_layer(self, layer):
338          MapRenderer.draw_shape_layer(self, layer)          MapRenderer.draw_shape_layer(self, layer)
339          if layer is self.selected_layer and self.selected_shape is not None:          if layer is self.selected_layer and self.selected_shapes:
340              pen = wxPen(wxBLACK, 3, wxSOLID)              pen = wxPen(wxBLACK, 3, wxSOLID)
341              brush = wxBrush(wxBLACK, wxCROSS_HATCH)              brush = wxBrush(wxBLACK, wxCROSS_HATCH)
342                
343              shapetype = layer.ShapeType()              shapetype = layer.ShapeType()
             index = self.selected_shape  
344              if shapetype == SHAPETYPE_POLYGON:              if shapetype == SHAPETYPE_POLYGON:
345                  self.draw_polygon_shape(layer, index, pen, brush)                  offx, offy = self.offset
346              else:                  renderparam = self.polygon_render_param(layer)
347                    func = self.draw_polygon_shape
348                    args = (pen, brush)
349                elif shapetype == SHAPETYPE_ARC:
350                    renderparam = self.polygon_render_param(layer)
351                    func = self.draw_polygon_shape
352                    args = (pen, None)
353                elif shapetype == SHAPETYPE_POINT:
354                    renderparam = layer
355                  self.dc.SetBrush(brush)                  self.dc.SetBrush(brush)
356                  self.dc.SetPen(pen)                  self.dc.SetPen(pen)
357                  if shapetype == SHAPETYPE_ARC:                  func = self.draw_point_shape
358                      f = self.draw_arc_shape                  args = ()
359                  elif shapetype == SHAPETYPE_POINT:              else:
360                      f = self.draw_point_shape                  raise TypeError(_("Unhandled shape type %s") % shapetype)
361                  f(layer, index)  
362                for index in self.selected_shapes:
363                    func(renderparam, index, *args)
364    
365    
366        def layer_ids(self, layer):
367            """Return the shapeids covered by the region that has to be redrawn
368    
369            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            x, y, width, height = self.update_region
388            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  class PrinterRender(MapRenderer):          return layer.ShapesInRegion((left, bottom, right, top))
402    
     # When printing we want to see all layers  
     honor_visibility = 0  
403    
404      RenderMap = MapRenderer.render_map  class ExportRenderer(ScreenRenderer):
405    
406        honor_visibility = 1
407            
408        def RenderMap(self, map, region, mapregion,
409                      selected_layer, selected_shapes ):
410            """Render the map.
411    
412            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                layers = map.Layers()
514                layers.reverse()
515                for l in layers:
516                    if l.Visible():
517                        # Render title
518                        dc.DrawText(l.Title(), posx, posy)
519                        posy+=stepy
520                        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            
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    

Legend:
Removed from v.6  
changed lines
  Added in v.1419

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26