/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/classification.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/Model/classification.py

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

revision 428 by jonathan, Mon Feb 24 18:46:35 2003 UTC revision 2657 by jan, Wed Jul 27 21:49:25 2005 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001 by Intevation GmbH  # Copyright (c) 2001, 2003, 2005 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jonathan Coles <[email protected]>  # Jonathan Coles <[email protected]>
4    # Jan-Oliver Wagner <[email protected]> (2005)
5  #  #
6  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
7  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
# Line 16  an input value falls with a range that d Line 17  an input value falls with a range that d
17  If no mapping can be found then default data will  If no mapping can be found then default data will
18  be returned. Input values must be hashable objects  be returned. Input values must be hashable objects
19    
20  See the description of GetClassData() for more information  See the description of FindGroup() for more information
21  on the mapping algorithm.  on the mapping algorithm.
22  """  """
23        
24  # fix for people using python2.1  import copy, operator, types
 from __future__ import nested_scopes  
   
 from messages import LAYER_PROJECTION_CHANGED, LAYER_LEGEND_CHANGED, \  
      LAYER_VISIBILITY_CHANGED  
25    
26  from Thuban import _  from Thuban import _
 from Thuban.Model.color import Color  
27    
28  from wxPython.wx import *  from messages import \
29        LAYER_PROJECTION_CHANGED, \
30        LAYER_LEGEND_CHANGED, \
31        LAYER_VISIBILITY_CHANGED,\
32        CLASS_CHANGED
33    
34  # constants  from Thuban.Model.color import Color, Transparent, Black
35  RANGE_MIN  = 0  from Thuban.Model.range import Range
 RANGE_MAX  = 1  
 RANGE_DATA = 2  
36    
37  class Classification:  import Thuban.Model.layer
38    
39      def __init__(self, layer = None, field = None):  from Thuban.Lib.connector import Publisher
         """Initialize a classification.  
40    
41             layer -- the layer object who owns this classification  class Classification(Publisher):
42        """Encapsulates the classification of layer.
43        
44        The Classification divides some kind of data into Groups which
45        are associated with properties. Later the properties can be
46        retrieved by matching data values to the appropriate group.
47        """
48    
49             field -- the name of the data table field that      def __init__(self):
50                      is to be used to classify layer properties          """Initialize a classification."""
         """  
51    
52          self.layer = layer          self.__groups = []
53          self.points = {}  
54          self.ranges = []          self.SetDefaultGroup(ClassGroupDefault())
         self.maps   = []  
         self.DefaultData = ClassDataDefault()  
         self.field = field  
         #self.SetField(field)  
55    
56      def __iter__(self):      def __iter__(self):
57          return ClassIterator(self.DefaultData,          return ClassIterator(self.__groups)
58                               self.points.values(),  
59                               self.ranges,      def __deepcopy__(self, memo):
60                               self.maps)          clazz = Classification()
61    
62      def __SendMessage(self, message):          clazz.__groups[0] = copy.deepcopy(self.__groups[0])
63          if self.layer is not None:  
64              self.layer.changed(message, self.layer)          for i in range(1, len(self.__groups)):
65                    clazz.__groups.append(copy.deepcopy(self.__groups[i]))
66      def SetField(self, field):  
67          """Set the name of the data table field to use.          return clazz
           
            field -- if None then all values map to the default data  
         """  
68    
69          self.field = field      def __SendNotification(self):
70          self.__SendMessage(LAYER_LEGEND_CHANGED)          """Notify the layer that this class has changed."""
71            self.issue(CLASS_CHANGED)
72    
73      def GetField(self):      def __getattr__(self, attr):
74          return self.field          """Generate the compiled classification on demand"""
75            if attr == "_compiled_classification":
76                self._compile_classification()
77                return self._compiled_classification
78            raise AttributeError(attr)
79    
80      def SetLayer(self, layer):      def _compile_classification(self):
81          self.layer = layer          """Generate the compiled classification
         self.__SendMessage(LAYER_LEGEND_CHANGED)  
82    
83      def GetLayer(self):          The compiled classification is a more compact representation of
84          return layer.self          the classification groups that is also more efficient for
85            performing the classification.
86    
87      def SetDefaultData(self, data):          The compiled classification is a list of tuples. The first
88          """Set the data to be used when a value can't be classified.          element of the tuple is a string which describes the rest of the
89            tuple. There are two kinds of tuples:
90    
91             data -- data that the value maps to. See class description.            'singletons'
92    
93                The second element of the tuple is a dictionary which
94                combines several consecutive ClassGroupSingleton instances.
95                The dictionary maps the values of the singletons (as
96                returned by the GetValue() method) to the corresponding
97                group.
98    
99              'range'
100    
101                The tuple describes a ClassGroupRange instance. The tuples
102                second element is a tuple fo the form (lfunc, min, max,
103                rfunc, group) where group is the original group object,
104                lfunc and rfuct are comparison functions and min and max are
105                lower and upper bounds of the range. Given a value and such
106                a tuple the group matches if and only if
107    
108                    lfunc(min, value) and rfunc(max, value)
109    
110                is true.
111    
112            The compiled classification is bound to
113            self._compile_classification.
114          """          """
115            compiled = []
116            for group in self.__groups[1:]:
117                if isinstance(group, ClassGroupSingleton):
118                    if not compiled or compiled[-1][0] != "singletons":
119                        compiled.append(("singletons", {}))
120                    compiled[-1][1].setdefault(group.GetValue(), group)
121                elif isinstance(group, ClassGroupRange):
122                    left, min, max, right = group.GetRangeTuple()
123                    if left == "[":
124                        lfunc = operator.le
125                    elif left == "]":
126                        lfunc = operator.lt
127                    if right == "[":
128                        rfunc = operator.gt
129                    elif right == "]":
130                        rfunc = operator.ge
131                    compiled.append(("range", (lfunc, min, max, rfunc, group)))
132                else:
133                    raise TypeError("Unknown group type %s", group)
134            self._compiled_classification = compiled
135    
136          assert(data.GetType() == ClassData.DEFAULT)      def _clear_compiled_classification(self):
137          self.DefaultData = data          """Reset the compiled classification.
138    
139      def GetDefaultData(self):          If will be created on demand when self._compiled_classification
140          return self.DefaultData          is accessed again.
141    
142            Call this method whenever self.__groups is modified.
143            """
144            try:
145                del self._compiled_classification
146            except:
147                pass
148    
149      #      #
150      # these SetDefault* methods are really only provided for      # these SetDefault* methods are really only provided for
# Line 103  class Classification: Line 153  class Classification:
153      #      #
154    
155      def SetDefaultFill(self, fill):      def SetDefaultFill(self, fill):
156          self.DefaultData.SetFill(fill)          """Set the default fill color.
157          self.__SendMessage(LAYER_LEGEND_CHANGED)  
158            fill -- a Color object.
159            """
160            self.GetDefaultGroup().GetProperties().SetFill(fill)
161            self.__SendNotification()
162                    
163      def GetDefaultFill(self):      def GetDefaultFill(self):
164          return self.DefaultData.GetFill()          """Return the default fill color."""
165            return self.GetDefaultGroup().GetProperties().GetFill()
166                    
167      def SetDefaultStroke(self, stroke):      def SetDefaultLineColor(self, color):
168          self.DefaultData.SetStroke(stroke)          """Set the default line color.
169          self.__SendMessage(LAYER_LEGEND_CHANGED)  
170            color -- a Color object.
171            """
172            self.GetDefaultGroup().GetProperties().SetLineColor(color)
173            self.__SendNotification()
174                    
175      def GetDefaultStroke(self):      def GetDefaultLineColor(self):
176          return self.DefaultData.GetStroke()          """Return the default line color."""
177            return self.GetDefaultGroup().GetProperties().GetLineColor()
178                    
179      def SetDefaultStrokeWidth(self, strokeWidth):      def SetDefaultLineWidth(self, lineWidth):
180          self.DefaultData.SetStrokeWidth(strokeWidth)          """Set the default line width.
181          self.__SendMessage(LAYER_LEGEND_CHANGED)  
182            lineWidth -- an integer > 0.
183            """
184            assert isinstance(lineWidth, types.IntType)
185            self.GetDefaultGroup().GetProperties().SetLineWidth(lineWidth)
186            self.__SendNotification()
187                    
188      def GetDefaultStrokeWidth(self):      def GetDefaultLineWidth(self):
189          return self.DefaultData.GetStrokeWidth()          """Return the default line width."""
190            return self.GetDefaultGroup().GetProperties().GetLineWidth()
191                    
     def AddClassData(self, item):  
         type = item.GetType()  
192    
193          if type == ClassData.POINT:      #
194              self.points[item.GetValue()] = item      # The methods that manipulate self.__groups have to be kept in
195          elif type == ClassData.RANGE:      # sync. We store the default group in index 0 to make it
196              self.ranges.append(item)      # convienent to iterate over the classification's groups, but
197          elif type == ClassData.MAP:      # from the user's perspective the first (non-default) group is
198              self.maps.append(item)      # at index 0 and the DefaultGroup is a special entity.
199          elif type == ClassData.DEFAULT:      #
200              self.DefaultData = item  
201        def SetDefaultGroup(self, group):
202            """Set the group to be used when a value can't be classified.
203    
204            group -- group that the value maps to.
205            """
206            assert isinstance(group, ClassGroupDefault)
207            if len(self.__groups) > 0:
208                self.__groups[0] = group
209          else:          else:
210              raise ValueError(_("Unrecognized ClassData type %s") % type)              self.__groups.append(group)
211            self.__SendNotification()
212    
213        def GetDefaultGroup(self):
214            """Return the default group."""
215            return self.__groups[0]
216    
217          self.__SendMessage(LAYER_LEGEND_CHANGED)      def AppendGroup(self, item):
218            """Append a new ClassGroup item to the classification.
219    
220            item -- this must be a valid ClassGroup object
221            """
222    
223      def GetClassData(self, value):          self.InsertGroup(self.GetNumGroups(), item)
         """Return the associated data, or the default data.  
224    
225             The following search technique is used:      def InsertGroup(self, index, group):
226                 (1) if the field is None, return the default data          assert isinstance(group, ClassGroup)
227                 (2) check if the value exists as a single value          self.__groups.insert(index + 1, group)
228                 (3) check if the value falls within a range. Ranges          self._clear_compiled_classification()
229                     are checked in the order they were added to          self.__SendNotification()
230                     the classification.  
231        def RemoveGroup(self, index):
232             value -- the value to classify. If there is no mapping,          """Remove the classification group with the given index"""
233                      or value is None, return the default properties          self.__groups.pop(index + 1)
234          """          self._clear_compiled_classification()
235            self.__SendNotification()
236          if self.field is not None and value is not None:  
237              #      def ReplaceGroup(self, index, group):
238              # check the discrete values          assert isinstance(group, ClassGroup)
239              #          self.__groups[index + 1] = group
240              if self.points.has_key(value):          self._clear_compiled_classification()
241                  return self.points[value]          self.__SendNotification()
242    
243              #      def GetGroup(self, index):
244              # check the ranges          return self.__groups[index + 1]
245              #  
246              for p in self.ranges:      def GetNumGroups(self):
247                  if p.InRange(value):          """Return the number of non-default groups in the classification."""
248                      return p          return len(self.__groups) - 1
   
             #  
             # check the maps  
             #  
             for p in self.maps:  
                 try:  
                     return p.Map(value)  
                 except: pass  
249    
250          return self.DefaultData      def FindGroup(self, value):
251            """Return the group that matches the value.
252    
253            Groups are effectively checked in the order the were added to
254            the Classification.
255    
256            value -- the value to classify. If there is no mapping or value
257                     is None, return the default properties
258            """
259    
260            if value is not None:
261                for typ, params in self._compiled_classification:
262                    if typ == "singletons":
263                        group = params.get(value)
264                        if group is not None:
265                            return group
266                    elif typ == "range":
267                        lfunc, min, max, rfunc, g = params
268                        if lfunc(min, value) and rfunc(max, value):
269                            return g
270    
271            return self.GetDefaultGroup()
272    
273        def GetProperties(self, value):
274            """Return the properties associated with the given value.
275          
276            Use this function rather than Classification.FindGroup().GetProperties()
277            since the returned group may be a ClassGroupMap which doesn't support
278            a call to GetProperties().
279            """
280    
281            group = self.FindGroup(value)
282            if isinstance(group, ClassGroupMap):
283                return group.GetPropertiesFromValue(value)
284            else:
285                return group.GetProperties()
286    
287      def TreeInfo(self):      def TreeInfo(self):
288          items = []          items = []
289    
290          def build_color_item(text, color):          def build_color_item(text, color):
291              if color is Color.None:              if color is Transparent:
292                  return ("%s: %s" % (text, _("None")), None)                  return ("%s: %s" % (text, _("None")), None)
293    
294              return ("%s: (%.3f, %.3f, %.3f)" %              return ("%s: (%.3f, %.3f, %.3f)" %
295                      (text, color.red, color.green, color.blue),                      (text, color.red, color.green, color.blue),
296                      color)                      color)
297    
298          def build_item(data, string):          def build_item(group, string):
299              label = data.GetLabel()              label = group.GetLabel()
300              if label == "":              if label == "":
301                  label = string                  label = string
302              else:              else:
303                  label += " (%s)" % string                  label += " (%s)" % string
304    
305                props = group.GetProperties()
306              i = []              i = []
307              v = data.GetStroke()              v = props.GetLineColor()
308              i.append(build_color_item(_("Stroke"), v))              i.append(build_color_item(_("Line Color"), v))
309              v = data.GetStrokeWidth()              v = props.GetLineWidth()
310              i.append(_("Stroke Width: %s") % v)              i.append(_("Line Width: %s") % v)
311              v = data.GetFill()  
312                # Note: Size is owned by all properties, so
313                # a size will also appear where it does not
314                # make sense like for lines and polygons.
315                v = props.GetSize()
316                i.append(_("Size: %s") % v)
317    
318                v = props.GetFill()
319              i.append(build_color_item(_("Fill"), v))              i.append(build_color_item(_("Fill"), v))
320              return (label, i)              return (label, i)
321    
322          for p in self:          for p in self:
323              type = p.GetType()              items.append(build_item(p, p.GetDisplayText()))
             if type == ClassData.DEFAULT:  
                 items.append(build_item(self.DefaultData, _("'DEFAULT'")))  
             elif type == ClassData.POINT:  
                 items.append(build_item(p, str(p.GetValue())))  
             elif type == ClassData.RANGE:  
                 items.append(build_item(p, "%s - %s" %  
                                            (p.GetMin(), p.GetMax())))  
   
 #       for p in self.points.values():  
 #           items.append(build_item(p, str(p.GetValue())))  
324    
325  #       for p in self.ranges:          return (_("Classification"), items)
 #           items.append(build_item(p, "%s - %s" % (p.GetMin(), p.GetMax())))  
326    
         return (_("Classifications"), items)  
   
327  class ClassIterator:  class ClassIterator:
328        """Allows the Groups in a Classifcation to be interated over.
329    
330        The items are returned in the following order:
331            default data, singletons, ranges, maps
332        """
333    
334        def __init__(self, data): #default, points, ranges, maps):
335            """Constructor.
336    
337            default -- the default group
338    
339            points -- a list of singleton groups
340    
341      def __init__(self, default, points, ranges, maps):          ranges -- a list of range groups
342          self.data = [default, points, ranges, maps]  
343          self.data_iter = iter(self.data)          maps -- a list of map groups
344          self.iter = None          """
345    
346            self.data = data
347            self.data_index = 0
348    
349      def __iter__(self):      def __iter__(self):
350          return self          return self
351    
352      def next(self):      def next(self):
353          if self.iter is None:          """Return the next item."""
             try:  
                 self.data_item = self.data_iter.next()  
                 self.iter = iter(self.data_item)  
             except TypeError:  
                 return self.data_item  
354    
355          try:          if self.data_index >= len(self.data):
356              return self.iter.next()              raise StopIteration
         except StopIteration:  
             self.iter = None  
             return self.next()  
         
 class ClassData:  
   
     INVALID = -1  
     DEFAULT = 0  
     POINT = 1  
     RANGE = 2  
     MAP   = 3  
   
     def __init__(self, classData = None, type = INVALID):  
   
         if classData is not None:  
             self.SetStroke(classData.GetStroke())  
             self.SetStrokeWidth(classData.GetStrokeWidth())  
             self.SetFill(classData.GetFill())  
357          else:          else:
358              self.SetStroke(Color.None)              d = self.data[self.data_index]
359              self.SetStrokeWidth(1)              self.data_index += 1
360              self.SetFill(Color.None)              return d
361    
362          self.type = type  class ClassGroupProperties:
363          self.label = ""      """Represents the properties of a single Classification Group.
       
     def GetType(self):  
         return self.type  
364    
365      def GetStroke(self):      These are used when rendering a layer."""
         return self.stroke  
366    
367      def SetStroke(self, stroke):      # TODO: Actually, size is only relevant for point objects.
368          assert(isinstance(stroke, Color))      # Eventually it should be spearated, e.g. when introducing symbols.
         self.stroke = stroke  
369    
370      def GetStrokeWidth(self):      def __init__(self, props = None):
371          return self.stroke_width          """Constructor.
372    
373      def SetStrokeWidth(self, stroke_width):          props -- a ClassGroupProperties object. The class is copied if
374          if (stroke_width < 1):                   prop is not None. Otherwise, a default set of properties
375              raise ValueError(_("stroke_width < 1"))                   is created such that: line color = Black, line width = 1,
376                     size = 5 and fill color = Transparent
377            """
378    
379            if props is not None:
380                self.SetProperties(props)
381            else:
382                self.SetLineColor(Black)
383                self.SetLineWidth(1)
384                self.SetSize(5)
385                self.SetFill(Transparent)
386    
387        def SetProperties(self, props):
388            """Set this class's properties to those in class props."""
389    
390            assert isinstance(props, ClassGroupProperties)
391            self.SetLineColor(props.GetLineColor())
392            self.SetLineWidth(props.GetLineWidth())
393            self.SetSize(props.GetSize())
394            self.SetFill(props.GetFill())
395    
396        def GetLineColor(self):
397            """Return the line color as a Color object."""
398            return self.__stroke
399    
400          self.stroke_width = stroke_width      def SetLineColor(self, color):
401            """Set the line color.
402    
403            color -- the color of the line. This must be a Color object.
404            """
405    
406            self.__stroke = color
407    
408        def GetLineWidth(self):
409            """Return the line width."""
410            return self.__strokeWidth
411    
412        def SetLineWidth(self, lineWidth):
413            """Set the line width.
414    
415            lineWidth -- the new line width. This must be > 0.
416            """
417            assert isinstance(lineWidth, types.IntType)
418            if (lineWidth < 1):
419                raise ValueError(_("lineWidth < 1"))
420    
421            self.__strokeWidth = lineWidth
422    
423        def GetSize(self):
424            """Return the size."""
425            return self.__size
426    
427        def SetSize(self, size):
428            """Set the size.
429    
430            size -- the new size. This must be > 0.
431            """
432            assert isinstance(size, types.IntType)
433            if (size < 1):
434                raise ValueError(_("size < 1"))
435    
436            self.__size = size
437    
438      def GetFill(self):      def GetFill(self):
439          return self.fill          """Return the fill color as a Color object."""
440            return self.__fill
441    
442      def SetFill(self, fill):      def SetFill(self, fill):
443          assert(isinstance(fill, Color))          """Set the fill color.
444          self.fill = fill  
445            fill -- the color of the fill. This must be a Color object.
446            """
447    
448            self.__fill = fill
449    
450        def __eq__(self, other):
451            """Return true if 'props' has the same attributes as this class"""
452    
453            #
454            # using 'is' over '==' results in a huge performance gain
455            # in the renderer
456            #
457            return isinstance(other, ClassGroupProperties)   \
458                and (self.__stroke is other.__stroke or      \
459                     self.__stroke == other.__stroke)        \
460                and (self.__fill is other.__fill or          \
461                     self.__fill == other.__fill)            \
462                and self.__strokeWidth == other.__strokeWidth\
463                and self.__size == other.__size
464    
465        def __ne__(self, other):
466            return not self.__eq__(other)
467    
468        def __copy__(self):
469            return ClassGroupProperties(self)
470    
471        def __deepcopy__(self):
472            return ClassGroupProperties(self)
473    
474        def __repr__(self):
475            return repr((self.__stroke, self.__strokeWidth, self.__size,
476                        self.__fill))
477    
478    class ClassGroup:
479        """A base class for all Groups within a Classification"""
480    
481        def __init__(self, label = "", props = None, group = None):
482            """Constructor.
483    
484            label -- A string representing the Group's label
485            """
486    
487            if group is not None:
488                self.SetLabel(copy.copy(group.GetLabel()))
489                self.SetProperties(copy.copy(group.GetProperties()))
490                self.SetVisible(group.IsVisible())
491            else:
492                self.SetLabel(label)
493                self.SetProperties(props)
494                self.SetVisible(True)
495    
496      def GetLabel(self):      def GetLabel(self):
497            """Return the Group's label."""
498          return self.label          return self.label
499    
500      def SetLabel(self, label):      def SetLabel(self, label):
501            """Set the Group's label.
502    
503            label -- a string representing the Group's label. This must
504                     not be None.
505            """
506            assert isinstance(label, types.StringTypes)
507          self.label = label          self.label = label
508    
509  class ClassDataDefault(ClassData):      def GetDisplayText(self):
510      def __init__(self, classData = None):          assert False, "GetDisplay must be overridden by subclass!"
511          ClassData.__init__(self, classData, ClassData.DEFAULT)          return ""
512    
513        def Matches(self, value):
514            """Determines if this Group is associated with the given value.
515    
516            Returns False. This needs to be overridden by all subclasses.
517            """
518            assert False, "GetMatches must be overridden by subclass!"
519            return False
520    
521        def GetProperties(self):
522            """Return the properties associated with the given value."""
523    
524            return self.prop
525    
526        def SetProperties(self, prop):
527            """Set the properties associated with this Group.
528    
529            prop -- a ClassGroupProperties object. if prop is None,
530                    a default set of properties is created.
531            """
532    
533            if prop is None: prop = ClassGroupProperties()
534            assert isinstance(prop, ClassGroupProperties)
535            self.prop = prop
536    
537        def IsVisible(self):
538            return self.visible
539    
540        def SetVisible(self, visible):
541            self.visible = visible
542    
543        def __eq__(self, other):
544            return isinstance(other, ClassGroup) \
545                and self.label == other.label \
546                and self.GetProperties() == other.GetProperties()
547    
548        def __ne__(self, other):
549            return not self.__eq__(other)
550    
551        def __repr__(self):
552            return repr(self.label) + ", " + repr(self.GetProperties())
553            
554  class ClassDataPoint(ClassData):  class ClassGroupSingleton(ClassGroup):
555        """A Group that is associated with a single value."""
556    
557        def __init__(self, value = 0, props = None, label = "", group = None):
558            """Constructor.
559    
560            value -- the associated value.
561    
562            prop -- a ClassGroupProperites object. If prop is None a default
563                     set of properties is created.
564    
565            label -- a label for this group.
566            """
567            ClassGroup.__init__(self, label, props, group)
568    
569            self.SetValue(value)
570    
571      def __init__(self, value = 0, classData = None):      def __copy__(self):
572          ClassData.__init__(self, classData, ClassData.POINT)          return ClassGroupSingleton(self.GetValue(),
573                                       self.GetProperties(),
574                                       self.GetLabel())
575    
576          self.value = value      def __deepcopy__(self, memo):
577            return ClassGroupSingleton(self.GetValue(), group = self)
578    
579      def GetValue(self):      def GetValue(self):
580          return self.value          """Return the associated value."""
581            return self.__value
582    
583      def SetValue(self, value):      def SetValue(self, value):
584          self.value = value          """Associate this Group with the given value."""
585            self.__value = value
586    
587        def Matches(self, value):
588            """Determine if the given value matches the associated Group value."""
589    
590            """Returns True if the value matches, False otherwise."""
591    
592            return self.__value == value
593    
594        def GetDisplayText(self):
595            label = self.GetLabel()
596    
597            if label != "": return label
598    
599            return str(self.GetValue())
600    
601        def __eq__(self, other):
602            return ClassGroup.__eq__(self, other) \
603                and isinstance(other, ClassGroupSingleton) \
604                and self.__value == other.__value
605    
606        def __repr__(self):
607            return "(" + repr(self.__value) + ", " + ClassGroup.__repr__(self) + ")"
608    
609    class ClassGroupDefault(ClassGroup):
610        """The default Group. When values do not match any other
611           Group within a Classification, the properties from this
612           class are used."""
613    
614  class ClassDataRange(ClassData):      def __init__(self, props = None, label = "", group = None):
615            """Constructor.
616    
617      def __init__(self, min = 0, max = 1, classData = None):          prop -- a ClassGroupProperites object. If prop is None a default
618          ClassData.__init__(self, classData, ClassData.RANGE)                   set of properties is created.
619    
620          if min >= max:          label -- a label for this group.
621              raise ValueError(_("ClassDataRange: %i(min) >= %i(max)!") %          """
622                               (min, max))  
623            ClassGroup.__init__(self, label, props, group)
624    
625        def __copy__(self):
626            return ClassGroupDefault(self.GetProperties(), self.GetLabel())
627    
628        def __deepcopy__(self, memo):
629            return ClassGroupDefault(label = self.GetLabel(), group = self)
630    
631        def Matches(self, value):
632            return True
633    
634        def GetDisplayText(self):
635            label = self.GetLabel()
636    
637            if label != "": return label
638    
639            return _("DEFAULT")
640    
641        def __eq__(self, other):
642            return ClassGroup.__eq__(self, other) \
643                and isinstance(other, ClassGroupDefault) \
644                and self.GetProperties() == other.GetProperties()
645    
646        def __repr__(self):
647            return "(" + ClassGroup.__repr__(self) + ")"
648    
649          self.SetRange(min, max)  class ClassGroupRange(ClassGroup):
650        """A Group that represents a range of values that map to the same
651           set of properties."""
652    
653        def __init__(self, _range = (0,1), props = None, label = "", group=None):
654            """Constructor.
655    
656            The minumum value must be strictly less than the maximum.
657    
658            _range -- either a tuple (min, max) where min < max or
659                      a Range object
660    
661            prop -- a ClassGroupProperites object. If prop is None a default
662                     set of properties is created.
663    
664            label -- a label for this group.
665            """
666    
667            ClassGroup.__init__(self, label, props, group)
668            self.SetRange(_range)
669    
670        def __copy__(self):
671            return ClassGroupRange(self.__range,
672                                   props = self.GetProperties(),
673                                   label = self.GetLabel())
674    
675        def __deepcopy__(self, memo):
676            return ClassGroupRange(copy.copy(self.__range),
677                                   group = self)
678    
679      def GetMin(self):      def GetMin(self):
680          return self.min          """Return the range's minimum value."""
681            return self.__range.GetRange()[1]
682    
683      def SetMin(self, min):      def SetMin(self, min):
684          self.SetRange(min, self.max)          """Set the range's minimum value.
685        
686            min -- the new minimum. Note that this must be less than the current
687                   maximum value. Use SetRange() to change both min and max values.
688            """
689        
690            self.SetRange((min, self.__range.GetRange()[2]))
691    
692      def GetMax(self):      def GetMax(self):
693          return self.max          """Return the range's maximum value."""
694            return self.__range.GetRange()[2]
695    
696      def SetMax(self, max):      def SetMax(self, max):
697          self.SetRange(self.min, max)          """Set the range's maximum value.
698        
699            max -- the new maximum. Note that this must be greater than the current
700                   minimum value. Use SetRange() to change both min and max values.
701            """
702            self.SetRange((self.__range.GetRange()[1], max))
703    
704        def SetRange(self, _range):
705            """Set a new range.
706    
707      def SetRange(self, min, max):          _range -- Either a tuple (min, max) where min < max or
708          self.min = min                    a Range object.
709          self.max = max  
710          if min >= max:          Raises ValueError on error.
711              raise ValueError(_("ClassDataRange: %i(min) >= %i(max)!") %          """
712                               (min, max))  
713            if isinstance(_range, Range):
714                self.__range = _range
715            elif isinstance(_range, types.TupleType) and len(_range) == 2:
716                self.__range = Range(("[", _range[0], _range[1], "["))
717            else:
718                raise ValueError()
719    
720      def GetRange(self):      def GetRange(self):
721          return (self.min, self.max)          """Return the range as a string"""
722            return self.__range.string(self.__range.GetRange())
723    
724        def GetRangeTuple(self):
725            return self.__range.GetRange()
726    
727        def Matches(self, value):
728            """Determine if the given value lies with the current range.
729    
730            The following check is used: min <= value < max.
731            """
732    
733            return operator.contains(self.__range, value)
734    
735        def GetDisplayText(self):
736            label = self.GetLabel()
737    
738            if label != "": return label
739    
740            return self.__range.string(self.__range.GetRange())
741    
742      def InRange(self, value):      def __eq__(self, other):
743          return self.min <= value < self.max          return ClassGroup.__eq__(self, other) \
744                and isinstance(other, ClassGroupRange) \
745                and self.__range == other.__range
746    
747  class ClassDataMap(ClassData):      def __repr__(self):
748            return "(" + str(self.__range) + ClassGroup.__repr__(self) + ")"
749    
750    class ClassGroupMap(ClassGroup):
751        """Currently, this class is not used."""
752    
753      FUNC_ID = "id"      FUNC_ID = "id"
754    
755      def __init__(self, map_type = FUNC_ID, func = None, classData = None):      def __init__(self, map_type = FUNC_ID, func = None, prop = None, label=""):
756          ClassData.__init__(self, classData, ClassData.MAP)          ClassGroup.__init__(self, label)
757    
758          self.map_type = map_type          self.map_type = map_type
759          self.func = func          self.func = func
# Line 369  class ClassDataMap(ClassData): Line 764  class ClassDataMap(ClassData):
764      def Map(self, value):      def Map(self, value):
765          return self.func(value)          return self.func(value)
766    
767        def GetProperties(self):
768            return None
769    
770        def GetPropertiesFromValue(self, value):
771            pass
772    
773        def GetDisplayText(self):
774            return "Map: " + self.map_type
775    
776      #      #
777      # built-in mappings      # built-in mappings
778      #      #

Legend:
Removed from v.428  
changed lines
  Added in v.2657

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26