/[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 484 by jonathan, Fri Mar 7 18:20:10 2003 UTC revision 627 by jonathan, Wed Apr 9 10:08:47 2003 UTC
# Line 16  an input value falls with a range that d Line 16  an input value falls with a range that d
16  If no mapping can be found then default 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 GetGroup() for more information  See the description of FindGroup() for more information
20  on the mapping algorithm.  on the mapping algorithm.
21  """  """
22        
# Line 25  from __future__ import nested_scopes Line 25  from __future__ import nested_scopes
25    
26  import copy  import copy
27    
28    from Thuban import _
29    
30  from types import *  from types import *
31    
32  from messages import LAYER_PROJECTION_CHANGED, LAYER_LEGEND_CHANGED, \  from messages import \
33       LAYER_VISIBILITY_CHANGED      LAYER_PROJECTION_CHANGED, \
34        LAYER_LEGEND_CHANGED, \
35        LAYER_VISIBILITY_CHANGED
36    
 from Thuban import _  
37  from Thuban.Model.color import Color  from Thuban.Model.color import Color
38    
39  import Thuban.Model.layer  import Thuban.Model.layer
40    
 # constants  
 RANGE_MIN  = 0  
 RANGE_MAX  = 1  
 RANGE_DATA = 2  
   
41  class Classification:  class Classification:
42      """Encapsulates the classification of layer. The Classification      """Encapsulates the classification of layer.
43      divides some kind of data into Groups which are associated with      
44      properties. Later the properties can be retrieved by matching      The Classification divides some kind of data into Groups which
45      data values to the appropriate group."""      are associated with properties. Later the properties can be
46        retrieved by matching data values to the appropriate group.
47        """
48    
49      def __init__(self, layer = None, field = None):      def __init__(self, layer = None, field = None):
50          """Initialize a classification.          """Initialize a classification.
51    
52             layer -- the Layer object who owns this classification          layer -- the Layer object who owns this classification
53    
54             field -- the name of the data table field that          field -- the name of the data table field that
55                      is to be used to classify layer properties                   is to be used to classify layer properties
56          """          """
57    
58          self.layer = None          self.layer = None
59          self.field = None          self.field = None
60          self.fieldType = None          self.fieldType = None
61          self.groups = []          self.__groups = []
62          self.__sendMessages = False  
63            self.__setLayerLock = False
64    
         self.__ToggleMessages(False)  
65          self.SetDefaultGroup(ClassGroupDefault())          self.SetDefaultGroup(ClassGroupDefault())
66    
67          self.SetLayer(layer)          self.SetLayer(layer)
68          self.SetField(field)          self.SetField(field)
69    
         self.__ToggleMessages(True)  
   
70      def __iter__(self):      def __iter__(self):
71          return ClassIterator(self.groups)          return ClassIterator(self.__groups)
72    
73        def __deepcopy__(self, memo):
74            clazz = Classification()
75    
76            # note: the only thing that isn't copied is the layer reference
77            clazz.field = self.field
78            clazz.fieldType = self.fieldType
79            clazz.__groups[0] = copy.deepcopy(self.__groups[0])
80    
81            for i in range(1, len(self.__groups)):
82                clazz.__groups.append(copy.deepcopy(self.__groups[i]))
83    
84      def __ToggleMessages(self, on):          return clazz
         self.__sendMessages = on  
85    
86      def __SendMessage(self, message):      def __SendNotification(self):
87          """Send the message 'message' to the parent layer."""          """Notify the layer that this class has changed."""
88          if self.__sendMessages and self.layer is not None:          if self.layer is not None:
89              self.layer.changed(message, self.layer)              self.layer.ClassChanged()
90            
91      def SetField(self, field = None):      def SetField(self, field):
92          """Set the name of the data table field to use.          """Set the name of the data table field to use.
93                    
94             If there is no layer then the field type is set to None,          If there is no layer then the field type is set to None,
95             otherwise the layer is queried to find the type of the          otherwise the layer is queried to find the type of the
96             field data          field data
97    
98             field -- if None then all values map to the default data          field -- if None then all values map to the default data
99          """          """
100    
101          if field == "":          if field == "":
102              field = None              field = None
103    
         self.field = field  
104    
105          if self.layer is not None:          if field is None:
106              fieldType = self.layer.GetFieldType(field)              if self.layer is not None:
107                    self.fieldType = None
108          else:          else:
109              fieldType = None              if self.layer is not None:
110                    fieldType = self.layer.GetFieldType(field)
111          self.SetFieldType(fieldType)                  if fieldType is None:
112                        raise ValueError("'%s' was not found in the layer's table."
113                                         % self.field)
114    
115                    #
116                    # unfortunately we cannot call SetFieldType() because it
117                    # requires the layer to be None
118                    #
119                    self.fieldType = fieldType
120                    #self.SetFieldType(fieldType)
121    
122          # XXX: if fieldType comes back None then field isn't in the table!          self.field = field
123    
124          self.__SendMessage(LAYER_LEGEND_CHANGED)          self.__SendNotification()
125    
126      def GetField(self):      def GetField(self):
127          """Return the name of the field."""          """Return the name of the field."""
# Line 114  class Classification: Line 132  class Classification:
132          return self.fieldType          return self.fieldType
133    
134      def SetFieldType(self, type):      def SetFieldType(self, type):
135          self.fieldType = type          """Set the type of the field used by this classification.
136    
137      def SetLayer(self, layer):          A ValueError is raised if the owning layer is not None and
138          """Set the owning Layer of this classification."""          'type' is different from the current field type.
139            """
140    
141          if __debug__:          if type != self.fieldType:
142              if layer is not None:              if self.layer is not None:
143                  assert(isinstance(layer, Thuban.Model.layer.Layer))                  raise ValueError()
144                else:
145                    self.fieldType = type
146                    self.__SendNotification()
147    
148        def SetLayer(self, layer):
149            """Set the owning Layer of this classification.
150    
151            A ValueError exception will be thrown either the field or
152            field type mismatch the information in the layer's table.
153            """
154    
155          # prevent infinite recursion when calling SetClassification()          # prevent infinite recursion when calling SetClassification()
156          if self.layer is not None and layer == self.layer:          if self.__setLayerLock: return
             return  
157    
158          self.layer = layer          self.__setLayerLock = True
         self.SetField(self.GetField()) # XXX: this sync's the fieldType  
159    
160          if self.layer is not None:          if layer is None:
161              self.layer.SetClassification(self)              if self.layer is not None:
162                    l = self.layer
163                    self.layer = None
164                    l.SetClassification(None)
165            else:
166                assert isinstance(layer, Thuban.Model.layer.Layer)
167    
168          #self.__SendMessage(LAYER_LEGEND_CHANGED)              old_layer = self.layer
169    
170      def GetLayer(self):              self.layer = layer
         """Return the parent layer."""  
         return self.layer  
171    
172      def SetDefaultGroup(self, group):              try:
173          """Set the group to be used when a value can't be classified.                  self.SetField(self.GetField()) # this sync's the fieldType
174                except ValueError:
175                    self.layer = old_layer
176                    self.__setLayerLock = False
177                    raise ValueError
178                else:
179                    self.layer.SetClassification(self)
180    
181             group -- group that the value maps to.          self.__setLayerLock = False
         """  
182    
183          assert(isinstance(group, ClassGroupDefault))      def GetLayer(self):
184          self.AddGroup(group)          """Return the parent layer."""
185            return self.layer
186    
     def GetDefaultGroup(self):  
         """Return the default group."""  
         return self.groups[0]  
187    
188      #      #
189      # these SetDefault* methods are really only provided for      # these SetDefault* methods are really only provided for
# Line 163  class Classification: Line 196  class Classification:
196    
197          fill -- a Color object.          fill -- a Color object.
198          """          """
199          assert(isinstance(fill, Color))          assert isinstance(fill, Color)
200          self.GetDefaultGroup().GetProperties().SetFill(fill)          self.GetDefaultGroup().GetProperties().SetFill(fill)
201          self.__SendMessage(LAYER_LEGEND_CHANGED)          self.__SendNotification()
202                    
203      def GetDefaultFill(self):      def GetDefaultFill(self):
204          """Return the default fill color."""          """Return the default fill color."""
# Line 176  class Classification: Line 209  class Classification:
209    
210          color -- a Color object.          color -- a Color object.
211          """          """
212          assert(isinstance(color, Color))          assert isinstance(color, Color)
213          self.GetDefaultGroup().GetProperties().SetLineColor(color)          self.GetDefaultGroup().GetProperties().SetLineColor(color)
214          self.__SendMessage(LAYER_LEGEND_CHANGED)          self.__SendNotification()
215                    
216      def GetDefaultLineColor(self):      def GetDefaultLineColor(self):
217          """Return the default line color."""          """Return the default line color."""
# Line 189  class Classification: Line 222  class Classification:
222    
223          lineWidth -- an integer > 0.          lineWidth -- an integer > 0.
224          """          """
225          assert(isinstance(lineWidth, IntType))          assert isinstance(lineWidth, IntType)
226          self.GetDefaultGroup().GetProperties().SetLineWidth(lineWidth)          self.GetDefaultGroup().GetProperties().SetLineWidth(lineWidth)
227          self.__SendMessage(LAYER_LEGEND_CHANGED)          self.__SendNotification()
228                    
229      def GetDefaultLineWidth(self):      def GetDefaultLineWidth(self):
230          """Return the default line width."""          """Return the default line width."""
231          return self.GetDefaultGroup().GetProperties().GetLineWidth()          return self.GetDefaultGroup().GetProperties().GetLineWidth()
232                    
233      def AddGroup(self, item):  
234          """Add a new ClassGroup item to the classification.      #
235        # The methods that manipulate self.__groups have to be kept in
236        # sync. We store the default group in index 0 to make it
237        # convienent to iterate over the classification's groups, but
238        # from the user's perspective the first (non-default) group is
239        # at index 0 and the DefaultGroup is a special entity.
240        #
241    
242        def SetDefaultGroup(self, group):
243            """Set the group to be used when a value can't be classified.
244    
245            group -- group that the value maps to.
246            """
247    
248            assert isinstance(group, ClassGroupDefault)
249            if len(self.__groups) > 0:
250                self.__groups[0] = group
251            else:
252                self.__groups.append(group)
253    
254        def GetDefaultGroup(self):
255            """Return the default group."""
256            return self.__groups[0]
257    
258        def AppendGroup(self, item):
259            """Append a new ClassGroup item to the classification.
260    
261          item -- this must be a valid ClassGroup object          item -- this must be a valid ClassGroup object
262          """          """
263    
264          assert(isinstance(item, ClassGroup))          self.InsertGroup(self.GetNumGroups(), item)
265    
266          if len(self.groups) > 0 and isinstance(item, ClassGroupDefault):      def InsertGroup(self, index, group):
267              self.groups[0] = item          
268              #self.SetDefaultGroup(item)          assert isinstance(group, ClassGroup)
269          else:  
270              self.groups.append(item)          self.__groups.insert(index + 1, group)
271    
272            self.__SendNotification()
273    
274        def RemoveGroup(self, index):
275            return self.__groups.pop(index + 1)
276    
277        def ReplaceGroup(self, index, group):
278            assert isinstance(group, ClassGroup)
279    
280            self.__groups[index + 1] = group
281    
282          self.__SendMessage(LAYER_LEGEND_CHANGED)          self.__SendNotification()
283    
284      def GetGroup(self, value):      def GetGroup(self, index):
285            return self.__groups[index + 1]
286    
287        def GetNumGroups(self):
288            """Return the number of non-default groups in the classification."""
289            return len(self.__groups) - 1
290    
291    
292        def FindGroup(self, value):
293          """Return the associated group, or the default group.          """Return the associated group, or the default group.
294    
295             Groups are checked in the order the were added to the          Groups are checked in the order the were added to the
296             Classification.          Classification.
297    
298             value -- the value to classify. If there is no mapping,          value -- the value to classify. If there is no mapping,
299                      the field is None or value is None,                   the field is None or value is None,
300                      return the default properties                   return the default properties
301          """          """
302    
303          if self.GetField() is not None and value is not None:          if self.GetField() is not None and value is not None:
304    
305              for i in range(1, len(self.groups)):              for i in range(1, len(self.__groups)):
306                  group = self.groups[i]                  group = self.__groups[i]
307                  if group.Matches(value):                  if group.Matches(value):
308                      return group                      return group
309    
310          return self.GetDefaultGroup()          return self.GetDefaultGroup()
311    
312      def GetProperties(self, value):      def GetProperties(self, value):
313          """Return the properties associated with the given value."""          """Return the properties associated with the given value.
314          
315            Use this function rather than Classification.FindGroup().GetProperties()
316            since the returned group may be a ClassGroupMap which doesn't support
317            a call to GetProperties().
318            """
319    
320          group = self.GetGroup(value)          group = self.FindGroup(value)
321          if isinstance(group, ClassGroupMap):          if isinstance(group, ClassGroupMap):
322              return group.GetPropertiesFromValue(value)              return group.GetPropertiesFromValue(value)
323          else:          else:
# Line 246  class Classification: Line 327  class Classification:
327          items = []          items = []
328    
329          def build_color_item(text, color):          def build_color_item(text, color):
330              if color is Color.None:              if color is Color.Transparent:
331                  return ("%s: %s" % (text, _("None")), None)                  return ("%s: %s" % (text, _("None")), None)
332    
333              return ("%s: (%.3f, %.3f, %.3f)" %              return ("%s: (%.3f, %.3f, %.3f)" %
# Line 271  class Classification: Line 352  class Classification:
352              return (label, i)              return (label, i)
353    
354          for p in self:          for p in self:
355              if isinstance(p, ClassGroupDefault):              items.append(build_item(p, p.GetDisplayText()))
356                  items.append(build_item(self.GetDefaultGroup(), _("'DEFAULT'")))  
357              elif isinstance(p, ClassGroupSingleton):  #           if isinstance(p, ClassGroupDefault):
358                  items.append(build_item(p, str(p.GetValue())))  #               items.append(build_item(self.GetDefaultGroup(), _("'DEFAULT'")))
359              elif isinstance(p, ClassGroupRange):  #           elif isinstance(p, ClassGroupSingleton):
360                  items.append(build_item(p, "%s - %s" %  #               items.append(build_item(p, str(p.GetValue())))
361                                             (p.GetMin(), p.GetMax())))  #           elif isinstance(p, ClassGroupRange):
362    #               items.append(build_item(p, "%s - %s" %
363    #                                          (p.GetMin(), p.GetMax())))
364    
365          return (_("Classification"), items)          return (_("Classification"), items)
366    
# Line 342  class ClassGroupProperties: Line 425  class ClassGroupProperties:
425          props -- a ClassGroupProperties object. The class is copied if          props -- a ClassGroupProperties object. The class is copied if
426                   prop is not None. Otherwise, a default set of properties                   prop is not None. Otherwise, a default set of properties
427                   is created such that: line color = Color.Black, line width = 1,                   is created such that: line color = Color.Black, line width = 1,
428                   and fill color = Color.None                   and fill color = Color.Transparent
429          """          """
430    
431          self.stroke = None          self.stroke = None
# Line 354  class ClassGroupProperties: Line 437  class ClassGroupProperties:
437          else:          else:
438              self.SetLineColor(Color.Black)              self.SetLineColor(Color.Black)
439              self.SetLineWidth(1)              self.SetLineWidth(1)
440              self.SetFill(Color.None)              self.SetFill(Color.Transparent)
441    
442      def SetProperties(self, props):      def SetProperties(self, props):
443          """Set this class's properties to those in class props."""          """Set this class's properties to those in class props."""
444    
445          assert(isinstance(props, ClassGroupProperties))          assert isinstance(props, ClassGroupProperties)
446          self.SetLineColor(props.GetLineColor())          self.SetLineColor(props.GetLineColor())
447          self.SetLineWidth(props.GetLineWidth())          self.SetLineWidth(props.GetLineWidth())
448          self.SetFill(props.GetFill())          self.SetFill(props.GetFill())
# Line 374  class ClassGroupProperties: Line 457  class ClassGroupProperties:
457          color -- the color of the line. This must be a Color object.          color -- the color of the line. This must be a Color object.
458          """          """
459    
460          assert(isinstance(color, Color))          assert isinstance(color, Color)
461          self.stroke = color          self.stroke = color
462    
463      def GetLineWidth(self):      def GetLineWidth(self):
# Line 386  class ClassGroupProperties: Line 469  class ClassGroupProperties:
469    
470          lineWidth -- the new line width. This must be > 0.          lineWidth -- the new line width. This must be > 0.
471          """          """
472          assert(isinstance(lineWidth, IntType))          assert isinstance(lineWidth, IntType)
473          if (lineWidth < 1):          if (lineWidth < 1):
474              raise ValueError(_("lineWidth < 1"))              raise ValueError(_("lineWidth < 1"))
475    
# Line 402  class ClassGroupProperties: Line 485  class ClassGroupProperties:
485          fill -- the color of the fill. This must be a Color object.          fill -- the color of the fill. This must be a Color object.
486          """          """
487    
488          assert(isinstance(fill, Color))          assert isinstance(fill, Color)
489          self.fill = fill          self.fill = fill
490    
491      def __eq__(self, other):      def __eq__(self, other):
# Line 419  class ClassGroupProperties: Line 502  class ClassGroupProperties:
502      def __copy__(self):      def __copy__(self):
503          return ClassGroupProperties(self)          return ClassGroupProperties(self)
504    
505        def __deepcopy__(self):
506            return ClassGroupProperties(self)
507    
508  class ClassGroup:  class ClassGroup:
509      """A base class for all Groups within a Classification"""      """A base class for all Groups within a Classification"""
510    
# Line 442  class ClassGroup: Line 528  class ClassGroup:
528          label -- a string representing the Group's label. This must          label -- a string representing the Group's label. This must
529                   not be None.                   not be None.
530          """          """
531          assert(isinstance(label, StringType))          assert isinstance(label, StringType)
532          self.label = label          self.label = label
533    
534        def GetDisplayText(self):
535            assert False, "GetDisplay must be overridden by subclass!"
536            return ""
537    
538      def Matches(self, value):      def Matches(self, value):
539          """Determines if this Group is associated with the given value.          """Determines if this Group is associated with the given value.
540    
541          Returns False. This needs to be overridden by all subclasses.          Returns False. This needs to be overridden by all subclasses.
542          """          """
543            assert False, "GetMatches must be overridden by subclass!"
544          return False          return False
545    
546      def GetProperties(self):      def GetProperties(self):
# Line 457  class ClassGroup: Line 548  class ClassGroup:
548    
549          Returns None. This needs to be overridden by all subclasses.          Returns None. This needs to be overridden by all subclasses.
550          """          """
551            assert False, "GetProperties must be overridden by subclass!"
552          return None          return None
553    
554            
# Line 519  class ClassGroupSingleton(ClassGroup): Line 611  class ClassGroupSingleton(ClassGroup):
611          """          """
612    
613          if prop is None: prop = ClassGroupProperties()          if prop is None: prop = ClassGroupProperties()
614          assert(isinstance(prop, ClassGroupProperties))          assert isinstance(prop, ClassGroupProperties)
615          self.prop = prop          self.prop = prop
616    
617        def GetDisplayText(self):
618            label = self.GetLabel()
619    
620            if label != "": return label
621    
622            return str(self.GetValue())
623    
624      def __eq__(self, other):      def __eq__(self, other):
625          return isinstance(other, ClassGroupSingleton) \          return isinstance(other, ClassGroupSingleton) \
626              and self.GetProperties() == other.GetProperties() \              and self.GetProperties() == other.GetProperties() \
# Line 569  class ClassGroupDefault(ClassGroup): Line 668  class ClassGroupDefault(ClassGroup):
668          """          """
669    
670          if prop is None: prop = ClassGroupProperties()          if prop is None: prop = ClassGroupProperties()
671          assert(isinstance(prop, ClassGroupProperties))          assert isinstance(prop, ClassGroupProperties)
672          self.prop = prop          self.prop = prop
673    
674        def GetDisplayText(self):
675            label = self.GetLabel()
676    
677            if label != "": return label
678    
679            return _("DEFAULT")
680    
681      def __eq__(self, other):      def __eq__(self, other):
682          return isinstance(other, ClassGroupDefault) \          return isinstance(other, ClassGroupDefault) \
683              and self.GetProperties() == other.GetProperties()              and self.GetProperties() == other.GetProperties()
# Line 681  class ClassGroupRange(ClassGroup): Line 787  class ClassGroupRange(ClassGroup):
787                  a default set of properties is created.                  a default set of properties is created.
788          """          """
789          if prop is None: prop = ClassGroupProperties()          if prop is None: prop = ClassGroupProperties()
790          assert(isinstance(prop, ClassGroupProperties))          assert isinstance(prop, ClassGroupProperties)
791          self.prop = prop          self.prop = prop
792    
793        def GetDisplayText(self):
794            label = self.GetLabel()
795    
796            if label != "": return label
797    
798            return _("%s - %s") % (self.GetMin(), self.GetMax())
799    
800      def __eq__(self, other):      def __eq__(self, other):
801          return isinstance(other, ClassGroupRange) \          return isinstance(other, ClassGroupRange) \
802              and self.GetProperties() == other.GetProperties() \              and self.GetProperties() == other.GetProperties() \
# Line 715  class ClassGroupMap(ClassGroup): Line 828  class ClassGroupMap(ClassGroup):
828      def GetPropertiesFromValue(self, value):      def GetPropertiesFromValue(self, value):
829          pass          pass
830    
831        def GetDisplayText(self):
832            return "Map: " + self.map_type
833    
834      #      #
835      # built-in mappings      # built-in mappings
836      #      #

Legend:
Removed from v.484  
changed lines
  Added in v.627

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26