/[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

trunk/thuban/Thuban/Model/classification.py revision 1426 by jonathan, Wed Jul 16 13:22:20 2003 UTC branches/WIP-pyshapelib-bramz/Thuban/Model/classification.py revision 2734 by bramz, Thu Mar 1 12:42:59 2007 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2003 by Intevation GmbH  # Copyright (c) 2001, 2003, 2005, 2006 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jonathan Coles <[email protected]>  # Jonathan Coles <[email protected]>
4    # Jan-Oliver Wagner <[email protected]> (2005)
5    # Frank Koormann <[email protected]> (2006)
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.
# Line 21  on the mapping algorithm. Line 23  on the mapping algorithm.
23  """  """
24        
25  import copy, operator, types  import copy, operator, types
26    import re
27    
28  from Thuban import _  from Thuban import _
29    
# Line 68  class Classification(Publisher): Line 71  class Classification(Publisher):
71      def __SendNotification(self):      def __SendNotification(self):
72          """Notify the layer that this class has changed."""          """Notify the layer that this class has changed."""
73          self.issue(CLASS_CHANGED)          self.issue(CLASS_CHANGED)
74        
75        def __getattr__(self, attr):
76            """Generate the compiled classification on demand"""
77            if attr == "_compiled_classification":
78                self._compile_classification()
79                return self._compiled_classification
80            raise AttributeError(attr)
81    
82        def _compile_classification(self):
83            """Generate the compiled classification
84    
85            The compiled classification is a more compact representation of
86            the classification groups that is also more efficient for
87            performing the classification.
88    
89            The compiled classification is a list of tuples. The first
90            element of the tuple is a string which describes the rest of the
91            tuple. There are two kinds of tuples:
92    
93              'singletons'
94    
95                The second element of the tuple is a dictionary which
96                combines several consecutive ClassGroupSingleton instances.
97                The dictionary maps the values of the singletons (as
98                returned by the GetValue() method) to the corresponding
99                group.
100    
101              'range'
102    
103                The tuple describes a ClassGroupRange instance. The tuples
104                second element is a tuple fo the form (lfunc, min, max,
105                rfunc, group) where group is the original group object,
106                lfunc and rfuct are comparison functions and min and max are
107                lower and upper bounds of the range. Given a value and such
108                a tuple the group matches if and only if
109    
110                    lfunc(min, value) and rfunc(max, value)
111    
112                is true.
113    
114              'pattern'
115                
116                The tuple contains the compiled regular expression object and
117                the original group object.
118    
119            The compiled classification is bound to
120            self._compile_classification.
121            """
122            compiled = []
123            for group in self.__groups[1:]:
124                if isinstance(group, ClassGroupSingleton):
125                    if not compiled or compiled[-1][0] != "singletons":
126                        compiled.append(("singletons", {}))
127                    compiled[-1][1].setdefault(group.GetValue(), group)
128                elif isinstance(group, ClassGroupPattern):
129                    pattern = re.compile(group.GetPattern())
130                    compiled.append(("pattern", (pattern, group)))
131                elif isinstance(group, ClassGroupRange):
132                    left, min, max, right = group.GetRangeTuple()
133                    if left == "[":
134                        lfunc = operator.le
135                    elif left == "]":
136                        lfunc = operator.lt
137                    if right == "[":
138                        rfunc = operator.gt
139                    elif right == "]":
140                        rfunc = operator.ge
141                    compiled.append(("range", (lfunc, min, max, rfunc, group)))
142                else:
143                    raise TypeError("Unknown group type %s", group)
144            self._compiled_classification = compiled
145    
146        def _clear_compiled_classification(self):
147            """Reset the compiled classification.
148    
149            If will be created on demand when self._compiled_classification
150            is accessed again.
151    
152            Call this method whenever self.__groups is modified.
153            """
154            try:
155                del self._compiled_classification
156            except:
157                pass
158    
159      #      #
160      # these SetDefault* methods are really only provided for      # these SetDefault* methods are really only provided for
161      # some backward compatibility. they should be considered      # some backward compatibility. they should be considered
# Line 126  class Classification(Publisher): Line 213  class Classification(Publisher):
213    
214          group -- group that the value maps to.          group -- group that the value maps to.
215          """          """
   
216          assert isinstance(group, ClassGroupDefault)          assert isinstance(group, ClassGroupDefault)
217          if len(self.__groups) > 0:          if len(self.__groups) > 0:
218              self.__groups[0] = group              self.__groups[0] = group
219          else:          else:
220              self.__groups.append(group)              self.__groups.append(group)
221            self.__SendNotification()
222    
223      def GetDefaultGroup(self):      def GetDefaultGroup(self):
224          """Return the default group."""          """Return the default group."""
# Line 148  class Classification(Publisher): Line 235  class Classification(Publisher):
235      def InsertGroup(self, index, group):      def InsertGroup(self, index, group):
236          assert isinstance(group, ClassGroup)          assert isinstance(group, ClassGroup)
237          self.__groups.insert(index + 1, group)          self.__groups.insert(index + 1, group)
238            self._clear_compiled_classification()
239          self.__SendNotification()          self.__SendNotification()
240    
241      def RemoveGroup(self, index):      def RemoveGroup(self, index):
242          return self.__groups.pop(index + 1)          """Remove the classification group with the given index"""
243            self.__groups.pop(index + 1)
244            self._clear_compiled_classification()
245            self.__SendNotification()
246    
247      def ReplaceGroup(self, index, group):      def ReplaceGroup(self, index, group):
248          assert isinstance(group, ClassGroup)          assert isinstance(group, ClassGroup)
249          self.__groups[index + 1] = group          self.__groups[index + 1] = group
250            self._clear_compiled_classification()
251          self.__SendNotification()          self.__SendNotification()
252    
253      def GetGroup(self, index):      def GetGroup(self, index):
# Line 166  class Classification(Publisher): Line 258  class Classification(Publisher):
258          return len(self.__groups) - 1          return len(self.__groups) - 1
259    
260      def FindGroup(self, value):      def FindGroup(self, value):
261          """Return the associated group, or the default group.          """Return the group that matches the value.
262    
263          Groups are checked in the order the were added to the          Groups are effectively checked in the order the were added to
264          Classification.          the Classification.
265    
266          value -- the value to classify. If there is no mapping,          value -- the value to classify. If there is no mapping or value
267                   the field is None or value is None,                   is None, return the default properties
                  return the default properties  
268          """          """
269    
270          if value is not None:          if value is not None:
271              for i in range(1, len(self.__groups)):              for typ, params in self._compiled_classification:
272                  group = self.__groups[i]                  if typ == "singletons":
273                  if group.Matches(value):                      group = params.get(value)
274                      return group                      if group is not None:
275                            return group
276                    elif typ == "range":
277                        lfunc, min, max, rfunc, g = params
278                        if lfunc(min, value) and rfunc(max, value):
279                            return g
280                    elif typ == "pattern":
281                        # TODO: make pattern more robust. The following chrashes
282                        # if accidently be applied on non-string columns.
283                        # Usually the UI prevents this.
284                        p, g = params
285                        if p.match(value):
286                            return g
287    
288          return self.GetDefaultGroup()          return self.GetDefaultGroup()
289    
# Line 222  class Classification(Publisher): Line 325  class Classification(Publisher):
325              i.append(build_color_item(_("Line Color"), v))              i.append(build_color_item(_("Line Color"), v))
326              v = props.GetLineWidth()              v = props.GetLineWidth()
327              i.append(_("Line Width: %s") % v)              i.append(_("Line Width: %s") % v)
328    
329                # Note: Size is owned by all properties, so
330                # a size will also appear where it does not
331                # make sense like for lines and polygons.
332                v = props.GetSize()
333                i.append(_("Size: %s") % v)
334    
335              v = props.GetFill()              v = props.GetFill()
336              i.append(build_color_item(_("Fill"), v))              i.append(build_color_item(_("Fill"), v))
337              return (label, i)              return (label, i)
# Line 230  class Classification(Publisher): Line 340  class Classification(Publisher):
340              items.append(build_item(p, p.GetDisplayText()))              items.append(build_item(p, p.GetDisplayText()))
341    
342          return (_("Classification"), items)          return (_("Classification"), items)
343    
344  class ClassIterator:  class ClassIterator:
345      """Allows the Groups in a Classifcation to be interated over.      """Allows the Groups in a Classifcation to be interated over.
346    
# Line 242  class ClassIterator: Line 352  class ClassIterator:
352          """Constructor.          """Constructor.
353    
354          default -- the default group          default -- the default group
355    
356          points -- a list of singleton groups          points -- a list of singleton groups
357    
358          ranges -- a list of range groups          ranges -- a list of range groups
359    
360          maps -- a list of map groups          maps -- a list of map groups
361          """          """
362    
# Line 265  class ClassIterator: Line 375  class ClassIterator:
375              d = self.data[self.data_index]              d = self.data[self.data_index]
376              self.data_index += 1              self.data_index += 1
377              return d              return d
378            
379  class ClassGroupProperties:  class ClassGroupProperties:
380      """Represents the properties of a single Classification Group.      """Represents the properties of a single Classification Group.
381      
382      These are used when rendering a layer."""      These are used when rendering a layer."""
383    
384        # TODO: Actually, size is only relevant for point objects.
385        # Eventually it should be spearated, e.g. when introducing symbols.
386    
387      def __init__(self, props = None):      def __init__(self, props = None):
388          """Constructor.          """Constructor.
389    
390          props -- a ClassGroupProperties object. The class is copied if          props -- a ClassGroupProperties object. The class is copied if
391                   prop is not None. Otherwise, a default set of properties                   prop is not None. Otherwise, a default set of properties
392                   is created such that: line color = Black, line width = 1,                   is created such that: line color = Black, line width = 1,
393                   and fill color = Transparent                   size = 5 and fill color = Transparent
394          """          """
395    
396          if props is not None:          if props is not None:
# Line 285  class ClassGroupProperties: Line 398  class ClassGroupProperties:
398          else:          else:
399              self.SetLineColor(Black)              self.SetLineColor(Black)
400              self.SetLineWidth(1)              self.SetLineWidth(1)
401                self.SetSize(5)
402              self.SetFill(Transparent)              self.SetFill(Transparent)
403    
404      def SetProperties(self, props):      def SetProperties(self, props):
# Line 293  class ClassGroupProperties: Line 407  class ClassGroupProperties:
407          assert isinstance(props, ClassGroupProperties)          assert isinstance(props, ClassGroupProperties)
408          self.SetLineColor(props.GetLineColor())          self.SetLineColor(props.GetLineColor())
409          self.SetLineWidth(props.GetLineWidth())          self.SetLineWidth(props.GetLineWidth())
410            self.SetSize(props.GetSize())
411          self.SetFill(props.GetFill())          self.SetFill(props.GetFill())
412            
413      def GetLineColor(self):      def GetLineColor(self):
414          """Return the line color as a Color object."""          """Return the line color as a Color object."""
415          return self.__stroke          return self.__stroke
# Line 322  class ClassGroupProperties: Line 437  class ClassGroupProperties:
437    
438          self.__strokeWidth = lineWidth          self.__strokeWidth = lineWidth
439    
440        def GetSize(self):
441            """Return the size."""
442            return self.__size
443    
444        def SetSize(self, size):
445            """Set the size.
446    
447            size -- the new size. This must be > 0.
448            """
449            assert isinstance(size, types.IntType)
450            if (size < 1):
451                raise ValueError(_("size < 1"))
452    
453            self.__size = size
454    
455      def GetFill(self):      def GetFill(self):
456          """Return the fill color as a Color object."""          """Return the fill color as a Color object."""
457          return self.__fill          return self.__fill
458    
459      def SetFill(self, fill):      def SetFill(self, fill):
460          """Set the fill color.          """Set the fill color.
461    
# Line 346  class ClassGroupProperties: Line 476  class ClassGroupProperties:
476                   self.__stroke == other.__stroke)        \                   self.__stroke == other.__stroke)        \
477              and (self.__fill is other.__fill or          \              and (self.__fill is other.__fill or          \
478                   self.__fill == other.__fill)            \                   self.__fill == other.__fill)            \
479              and self.__strokeWidth == other.__strokeWidth              and self.__strokeWidth == other.__strokeWidth\
480                and self.__size == other.__size
481    
482      def __ne__(self, other):      def __ne__(self, other):
483          return not self.__eq__(other)          return not self.__eq__(other)
# Line 358  class ClassGroupProperties: Line 489  class ClassGroupProperties:
489          return ClassGroupProperties(self)          return ClassGroupProperties(self)
490    
491      def __repr__(self):      def __repr__(self):
492          return repr((self.__stroke, self.__strokeWidth, self.__fill))          return repr((self.__stroke, self.__strokeWidth, self.__size,
493                        self.__fill))
494    
495  class ClassGroup:  class ClassGroup:
496      """A base class for all Groups within a Classification"""      """A base class for all Groups within a Classification"""
# Line 381  class ClassGroup: Line 513  class ClassGroup:
513      def GetLabel(self):      def GetLabel(self):
514          """Return the Group's label."""          """Return the Group's label."""
515          return self.label          return self.label
516    
517      def SetLabel(self, label):      def SetLabel(self, label):
518          """Set the Group's label.          """Set the Group's label.
519    
# Line 606  class ClassGroupRange(ClassGroup): Line 738  class ClassGroupRange(ClassGroup):
738          """Return the range as a string"""          """Return the range as a string"""
739          return self.__range.string(self.__range.GetRange())          return self.__range.string(self.__range.GetRange())
740    
741        def GetRangeTuple(self):
742            return self.__range.GetRange()
743    
744      def Matches(self, value):      def Matches(self, value):
745          """Determine if the given value lies with the current range.          """Determine if the given value lies with the current range.
746    
# Line 629  class ClassGroupRange(ClassGroup): Line 764  class ClassGroupRange(ClassGroup):
764      def __repr__(self):      def __repr__(self):
765          return "(" + str(self.__range) + ClassGroup.__repr__(self) + ")"          return "(" + str(self.__range) + ClassGroup.__repr__(self) + ")"
766    
767    class ClassGroupPattern(ClassGroup):
768        """A Group that is associated with a reg exp pattern."""
769    
770        def __init__(self, pattern = "", props = None, label = "", group = None):
771            """Constructor.
772    
773            pattern -- the associated pattern.
774    
775            props   -- a ClassGroupProperites object. If props is None a default
776                       set of properties is created.
777    
778            label   -- a label for this group.
779            """
780            ClassGroup.__init__(self, label, props, group)
781    
782            self.SetPattern(pattern)
783    
784        def __copy__(self):
785            return ClassGroupPattern(self.GetPattern(),
786                                       self.GetProperties(),
787                                       self.GetLabel())
788    
789        def __deepcopy__(self, memo):
790            return ClassGroupPattern(self.GetPattern(), group = self)
791    
792        def GetPattern(self):
793            """Return the associated pattern."""
794            return self.__pattern
795    
796        def SetPattern(self, pattern):
797            """Associate this Group with the given pattern."""
798            self.__pattern = pattern
799    
800        def Matches(self, pattern):
801            """Check if the given pattern matches the associated Group pattern."""
802    
803            """Returns True if the value matches, False otherwise."""
804    
805            if re.match(self.__pattern, pattern):
806                return True
807            else:
808                return False
809    
810        def GetDisplayText(self):
811            label = self.GetLabel()
812    
813            if label != "": return label
814    
815            return str(self.GetPattern())
816    
817        def __eq__(self, other):
818            return ClassGroup.__eq__(self, other) \
819                and isinstance(other, ClassGroupPattern) \
820                and self.__pattern == other.__pattern
821    
822        def __repr__(self):
823            return "(" + repr(self.__pattern) + ", " + ClassGroup.__repr__(self) + ")"
824    
825  class ClassGroupMap(ClassGroup):  class ClassGroupMap(ClassGroup):
826      """Currently, this class is not used."""      """Currently, this class is not used."""
827    

Legend:
Removed from v.1426  
changed lines
  Added in v.2734

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26