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

Legend:
Removed from v.371  
changed lines
  Added in v.1336

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26