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

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

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

revision 1866 by bh, Mon Oct 27 13:01:58 2003 UTC revision 2551 by jonathan, Thu Jan 27 14:19:41 2005 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH  # Copyright (c) 2001, 2002, 2003, 2004 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4  # Jonathan Coles <[email protected]>  # Jonathan Coles <[email protected]>
# Line 20  __version__ = "$Revision$" Line 20  __version__ = "$Revision$"
20  # $Source$  # $Source$
21  # $Id$  # $Id$
22    
23    import sys
24  import traceback  import traceback
25    
26  from Thuban.Model.layer import Layer, RasterLayer  from Thuban.Model.layer import Layer, RasterLayer
# Line 33  if Thuban.Model.resource.has_gdal_suppor Line 34  if Thuban.Model.resource.has_gdal_suppor
34      from gdalwarp import ProjectRasterFile      from gdalwarp import ProjectRasterFile
35    
36    
37    #
38    #       Renderer Extensions
39    #
40    # The renderer extensions provide a way to render layer types defined in
41    # Thuban extensions. The renderer extensions are stored as a list with
42    # (layer_class, draw_function) pairs. If the renderer has to draw a
43    # non-builtin layer type, i.e. a layer that is not a subclass of Layer
44    # or RasterLayer, it iterates through that list, tests whether the layer
45    # to be drawn is an instance of layer_class and if so calls
46    # draw_function with the renderer and the layer as arguments. Since
47    # drawing is done incrementally, the draw_function should return an
48    # iterable. The easiest way is to simply implement the draw_function as
49    # a generator and to yield in suitable places, or to return the empty
50    # tuple.
51    #
52    # New renderer extensions should be added with add_renderer_extension().
53    # If necessary the extensions list can be reset with
54    # init_renderer_extensions().
55    
56    _renderer_extensions = []
57    
58    def add_renderer_extension(layer_class, function):
59        """Add a renderer extension for the layer class layer_class
60    
61        When an instance of layer_class is to be drawn by the renderer the
62        renderer will call function with the renderer and the layer_class
63        instance as arguments. Since drawing is done incrementally, the
64        function should return an iterable. The easiest way is to simply
65        implement the draw_function as a generator and to yield True in
66        suitable places, or to return the empty tuple if it's not possible
67        to do the rendering incrementally.
68        """
69        _renderer_extensions.append((layer_class, function))
70    
71    def init_renderer_extensions():
72        """(Re)initialize the list of renderer extensions
73    
74        Calling this function outside of the test suite is probably not
75        useful.
76        """
77        del _renderer_extensions[:]
78    
79    def proj_params_to_str(proj):
80        "Build a string suitable for GDAL describing the given projection"
81        str = ""
82        if proj is not None:
83            for p in proj.GetAllParameters():
84                str += "+" + p + " "
85        return str
86    
87    #
88    #       Base Renderer
89    #
90    
91  class BaseRenderer:  class BaseRenderer:
92    
93      """Basic Renderer Infrastructure for Thuban Maps      """Basic Renderer Infrastructure for Thuban Maps
# Line 129  class BaseRenderer: Line 184  class BaseRenderer:
184          that methods especially in derived classes have access to the          that methods especially in derived classes have access to the
185          map if necessary.          map if necessary.
186          """          """
         # Whether the raster layer has already been drawn. See below for  
         # the optimization this is used for  
         seenRaster = True  
   
         #  
         # This is only a good optimization if there is only one  
         # raster layer and the image covers the entire window (as  
         # it currently does). We note if there is a raster layer  
         # and only begin drawing layers once we have drawn it.  
         # That way we avoid drawing layers that won't be seen.  
         #  
         if Thuban.Model.resource.has_gdal_support():  
             for layer in self.map.Layers():  
                 if isinstance(layer, RasterLayer) and layer.Visible():  
                     seenRaster = False  
                     break  
187    
188          for layer in self.map.Layers():          for layer in self.map.Layers():
189              # if honor_visibility is true, only draw visible layers,              # if honor_visibility is true, only draw visible layers,
190              # otherwise draw all layers              # otherwise draw all layers
191              if not self.honor_visibility or layer.Visible():              if not self.honor_visibility or layer.Visible():
192                  if isinstance(layer, Layer) and seenRaster:                  if isinstance(layer, Layer):
193                      for i in self.draw_shape_layer_incrementally(layer):                      for i in self.draw_shape_layer_incrementally(layer):
194                          yield True                          yield True
195                  elif isinstance(layer, RasterLayer) \                  elif isinstance(layer, RasterLayer) \
196                      and Thuban.Model.resource.has_gdal_support():                      and Thuban.Model.resource.has_gdal_support():
197                      self.draw_raster_layer(layer)                      self.draw_raster_layer(layer)
                     seenRaster = True  
198                      yield True                      yield True
199                    else:
200                        # look it up in the renderer extensions
201                        for cls, func in _renderer_extensions:
202                            if isinstance(layer, cls):
203                                for i in func(self, layer):
204                                    yield True
205                                break
206                        else:
207                            # No renderer found. Print a message about it
208                            print >>sys.stderr, ("Drawing layer %r not supported"
209                                                 % layer)
210                yield True
211    
212          self.draw_label_layer(self.map.LabelLayer())          self.draw_label_layer(self.map.LabelLayer())
213          yield False          yield False
# Line 184  class BaseRenderer: Line 234  class BaseRenderer:
234          defaultGroup = lc.GetDefaultGroup()          defaultGroup = lc.GetDefaultGroup()
235          table = layer.ShapeStore().Table()          table = layer.ShapeStore().Table()
236    
237            if lc.GetNumGroups() == 0:
238                # There's only the default group, so we can pretend that
239                # there is no field to classifiy on which makes things
240                # faster since we don't need the attribute information at
241                # all.
242                field = None
243    
244          # Determine which render function to use.          # Determine which render function to use.
245          useraw, draw_func, draw_func_param = self.low_level_renderer(layer)          useraw, draw_func, draw_func_param = self.low_level_renderer(layer)
246    
247            #
248          # Iterate through all shapes that have to be drawn.          # Iterate through all shapes that have to be drawn.
249            #
250    
251            # Count the shapes drawn so that we can yield every few hundred
252            # shapes
253          count = 0          count = 0
254    
255            # Cache the tools (pens and brushes) for the classification
256            # groups. This is a mapping from the group's ids to the a tuple
257            # (pen, brush)
258            tool_cache = {}
259    
260          for shape in self.layer_shapes(layer):          for shape in self.layer_shapes(layer):
261              count += 1              count += 1
262              if field is None:              if field is None:
263                  group = defaultGroup                  group = defaultGroup
264              else:              else:
265                  record = table.ReadRowAsDict(shape.ShapeID())                  value = table.ReadValue(shape.ShapeID(), field)
266                  assert record is not None                  group = lc.FindGroup(value)
                 group = lc.FindGroup(record[field])  
267    
268              if not group.IsVisible():              if not group.IsVisible():
269                  continue                  continue
270    
271              # don't recreate new objects if they are the same as before              try:
272              if group is not old_group:                  pen, brush = tool_cache[id(group)]
273                  old_group = group              except KeyError:
274                    pen, brush = tool_cache[id(group)] \
275                  prop = group.GetProperties()                               = self.tools_for_property(group.GetProperties())
   
                 if prop != old_prop:  
                     pen, brush = self.tools_for_property(prop)  
276    
277              if useraw:              if useraw:
278                  data = shape.RawData()                  data = shape.RawData()
279              else:              else:
280                  data = shape.Points()                  data = shape.Points()
281              draw_func(draw_func_param, data, pen, brush)              if draw_func == self.draw_point_shape:
282                     draw_func(draw_func_param, data, pen, brush,
283                               size = group.GetProperties().GetSize())
284                else:
285                     draw_func(draw_func_param, data, pen, brush)
286              if count % 500 == 0:              if count % 500 == 0:
287                  yield True                  yield True
288    
# Line 291  class BaseRenderer: Line 359  class BaseRenderer:
359          scale = self.scale          scale = self.scale
360          offx, offy = self.offset          offx, offy = self.offset
361          make_point = self.make_point          make_point = self.make_point
362    
363          for part in points:          for part in points:
364              result.append([])              result.append([])
365              for x, y in part:              for x, y in part:
# Line 309  class BaseRenderer: Line 378  class BaseRenderer:
378          value of the shape's Points() method. The coordinates in the          value of the shape's Points() method. The coordinates in the
379          DC's coordinate system are determined with          DC's coordinate system are determined with
380          self.projected_points.          self.projected_points.
381    
382            For a description of the algorithm look in wxproj.cpp.
383          """          """
384          points = self.projected_points(layer, points)          points = self.projected_points(layer, points)
385    
# Line 317  class BaseRenderer: Line 388  class BaseRenderer:
388              for part in points:              for part in points:
389                  polygon.extend(part)                  polygon.extend(part)
390    
391                # missing back vertices for correct filling.
392              insert_index = len(polygon)              insert_index = len(polygon)
393              for part in points[:-1]:              for part in points[:-1]:
394                  polygon.insert(insert_index, part[0])                  polygon.insert(insert_index, part[0])
# Line 346  class BaseRenderer: Line 418  class BaseRenderer:
418          for part in points:          for part in points:
419              self.dc.DrawLines(part)              self.dc.DrawLines(part)
420    
421      def draw_point_shape(self, layer, points, pen, brush):      def draw_point_shape(self, layer, points, pen, brush, size = 5):
422          """Draw a point shape from layer with the given brush and pen          """Draw a point shape from layer with the given brush and pen
423    
424          The shape is given by points argument which is a the return          The shape is given by points argument which is a the return
# Line 360  class BaseRenderer: Line 432  class BaseRenderer:
432          if not points:          if not points:
433              return              return
434    
435          radius = self.resolution * 5          radius = int(round(self.resolution * size))
436          self.dc.SetBrush(brush)          self.dc.SetBrush(brush)
437          self.dc.SetPen(pen)          self.dc.SetPen(pen)
438          for part in points:          for part in points:
# Line 380  class BaseRenderer: Line 452  class BaseRenderer:
452          offx, offy = self.offset          offx, offy = self.offset
453          width, height = self.dc.GetSizeTuple()          width, height = self.dc.GetSizeTuple()
454    
455          in_proj = ""          in_proj  = proj_params_to_str(layer.GetProjection())
456          proj = layer.GetProjection()          out_proj = proj_params_to_str(self.map.GetProjection())
         if proj is not None:  
             for p in proj.GetAllParameters():  
                 in_proj += "+" + p + " "  
457    
458          out_proj = ""          # True  -- warp the image to the size of the whole screen
459          proj = self.map.GetProjection()          # False -- only use the bound box of the layer (currently inaccurate)
460          if proj is not None:          if True:
461              for p in proj.GetAllParameters():          #if False:
462                  out_proj += "+" + p + " "              pmin = [0,height]
463                pmax = [width, 0]
464            else:
465                bb = layer.LatLongBoundingBox()
466                bb = [[[bb[0], bb[1]], [bb[2], bb[3]],],]
467                pmin, pmax = self.projected_points(layer, bb)[0]
468    
469            #print bb
470            #print pmin, pmax
471    
472            fmin = [max(0, min(pmin[0], pmax[0])) - offx,
473                    offy - min(height, max(pmin[1], pmax[1]))]
474    
475            fmax = [min(width, max(pmin[0], pmax[0])) - offx,
476                    offy - max(0, min(pmin[1], pmax[1]))]
477    
478            xmin = fmin[0]/self.scale
479            ymin = fmin[1]/self.scale
480            xmax = fmax[0]/self.scale
481            ymax = fmax[1]/self.scale
482    
483          xmin = (0 - offx) / self.scale          width  = int(min(width,  round(fmax[0] - fmin[0] + 1)))
484          ymin = (offy - height) / self.scale          height = int(min(height, round(fmax[1] - fmin[1] + 1)))
         xmax = (width - offx) / self.scale  
         ymax = (offy - 0) / self.scale  
485    
486          try:          try:
487              data = ProjectRasterFile(layer.GetImageFilename(),              project_params = (layer.GetImageFilename(), in_proj, out_proj,
488                                       in_proj, out_proj,                                (xmin, ymin, xmax, ymax), "", (width, height),
489                                       (xmin, ymin, xmax, ymax), "",                                layer.UseMask())
490                                       (width, height))  
491                data = (width, height, apply(ProjectRasterFile, project_params))
492    
493          except (IOError, AttributeError, ValueError):          except (IOError, AttributeError, ValueError):
494              # Why does this catch AttributeError and ValueError?              # Why does this catch AttributeError and ValueError?
495              # FIXME: The exception should be communicated to the user              # FIXME: The exception should be communicated to the user
496              # better.              # better.
497              traceback.print_exc()              traceback.print_exc()
498          else:          else:
499              self.draw_raster_data(data)              self.draw_raster_data(fmin[0]+offx, offy-fmax[1], data, "RAW")
500                data = None
     def draw_raster_data(self, data):  
         """Draw the raster image in data onto the DC  
   
         The raster image data is a string holding the data in BMP  
         format. The data is exactly the size of the dc and covers it  
         completely.  
501    
502          This method has to be implemented by derived classes.      def draw_raster_data(self, x, y, data, format="BMP"):
503            """Draw the raster image in data onto the DC with the top
504            left corner at (x,y)
505    
506            The raster image data is a tuple of the form
507                (width, height, (image_data, mask_data))
508            
509            holding the image width, height, image data, and mask data.
510            mask_data may be None if a mask should not be used. Both kinds
511            of data are assumed to be in the format specified in format.
512    
513            The format parameter is a string with the name of the format.
514            The following format names should be used:
515    
516              'RAW'  -- an array of RGB values (len=3*width*height)
517              'BMP'  -- Windows Bitmap
518              'JPEG' -- JPEG Image
519    
520            The default format is 'BMP'.
521    
522            This method has to be implemented by derived classes. The
523            implementation in the derived class should try to support at
524            least the formats specified above and may support more.
525          """          """
526          raise NotImplementedError          raise NotImplementedError
527    
# Line 447  class BaseRenderer: Line 551  class BaseRenderer:
551              text = label.text              text = label.text
552              if forward:              if forward:
553                  x, y = forward(x, y)                  x, y = forward(x, y)
554              x = x * scale + offx              x = int(round(x * scale + offx))
555              y = -y * scale + offy              y = int(round(-y * scale + offy))
556              width, height = self.dc.GetTextExtent(text)              width, height = self.dc.GetTextExtent(text)
557              if label.halign == ALIGN_LEFT:              if label.halign == ALIGN_LEFT:
558                  # nothing to be done                  # nothing to be done

Legend:
Removed from v.1866  
changed lines
  Added in v.2551

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26