/[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 397 by jonathan, Tue Feb 11 14:23:32 2003 UTC revision 449 by jonathan, Tue Mar 4 10:33:08 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 getProperties() for more information  See the description of GetGroup() for more information
20  on the mapping algorithm.  on the mapping algorithm.
21  """  """
22        
23  # fix for people using python2.1  # fix for people using python2.1
24  from __future__ import nested_scopes  from __future__ import nested_scopes
25    
26    from types import *
27    
28  from messages import LAYER_PROJECTION_CHANGED, LAYER_LEGEND_CHANGED, \  from messages import LAYER_PROJECTION_CHANGED, LAYER_LEGEND_CHANGED, \
29       LAYER_VISIBILITY_CHANGED       LAYER_VISIBILITY_CHANGED
30    
31  from Thuban import _  from Thuban import _
32  from Thuban.Model.color import Color  from Thuban.Model.color import Color
33    
34    import Thuban.Model.layer
35    
36  from wxPython.wx import *  from wxPython.wx import *
37    
38  # constants  # constants
# Line 38  RANGE_DATA = 2 Line 42  RANGE_DATA = 2
42    
43  class Classification:  class Classification:
44    
45      def __init__(self, layer, field = None):      def __init__(self, layer = None, field = None):
46          """Initialize a classification.          """Initialize a classification.
47    
48             layer -- the layer object who owns this classification             layer -- the layer object who owns this classification
# Line 47  class Classification: Line 51  class Classification:
51                      is to be used to classify layer properties                      is to be used to classify layer properties
52          """          """
53    
54            self.layer = None # stop message sending
55    
56            self.SetDefaultGroup(ClassGroupDefault())
57            self.SetField(field)
58    
59          self.layer = layer          self.layer = layer
60          self.points = {}          self.points = []
61          self.ranges = []          self.ranges = []
62          self.DefaultData = ClassData()          self.maps   = []
         self.field = field  
         #self.SetField(field)  
63    
64        def __iter__(self):
65            return ClassIterator(self.DefaultGroup,
66                                 self.points,
67                                 self.ranges,
68                                 self.maps)
69    
70        def __SendMessage(self, message):
71            if self.layer is not None:
72                self.layer.changed(message, self.layer)
73        
74      def SetField(self, field):      def SetField(self, field):
75          """Set the name of the data table field to use.          """Set the name of the data table field to use.
76                    
77             field -- if None then all values map to the default data             field -- if None then all values map to the default data
78          """          """
79    
80            if field == "":
81                field = None
82    
83          self.field = field          self.field = field
84          self.layer.changed(LAYER_LEGEND_CHANGED, self.layer)          self.__SendMessage(LAYER_LEGEND_CHANGED)
85    
86      def GetField(self):      def GetField(self):
87          return self.field          return self.field
88    
89      def SetDefaultData(self, data):      def SetLayer(self, layer):
90          """Set the data to be used when a value can't be classified.          assert(isinstance(layer, Thuban.Model.layer.Layer))
91            self.layer = layer
92            self.__SendMessage(LAYER_LEGEND_CHANGED)
93    
94        def GetLayer(self):
95            return layer.self
96    
97        def SetDefaultGroup(self, group):
98            """Set the group to be used when a value can't be classified.
99    
100             data -- data that the value maps to. See class description.             group -- group that the value maps to. See class description.
101          """          """
102    
103          self.DefaultData = data          assert(isinstance(group, ClassGroupDefault))
104            self.DefaultGroup = group
105    
106      def GetDefaultData(self):      def GetDefaultGroup(self):
107          return self.DefaultData          return self.DefaultGroup
108    
109        #
110        # these SetDefault* methods are really only provided for
111        # some backward compatibility. they should be considered
112        # for removal once all the classification code is finished.
113        #
114    
115      def SetDefaultFill(self, fill):      def SetDefaultFill(self, fill):
116          self.DefaultData.SetFill(fill)          assert(isinstance(fill, Color))
117          self.layer.changed(LAYER_LEGEND_CHANGED, self.layer)          self.DefaultGroup.GetProperties().SetFill(fill)
118            self.__SendMessage(LAYER_LEGEND_CHANGED)
119                    
120      def GetDefaultFill(self):      def GetDefaultFill(self):
121          return self.DefaultData.GetFill()          return self.DefaultGroup.GetProperties().GetFill()
122                    
123      def SetDefaultStroke(self, stroke):      def SetDefaultStroke(self, stroke):
124          self.DefaultData.SetStroke(stroke)          assert(isinstance(stroke, Color))
125          self.layer.changed(LAYER_LEGEND_CHANGED, self.layer)          self.DefaultGroup.GetProperties().SetStroke(stroke)
126            self.__SendMessage(LAYER_LEGEND_CHANGED)
127                    
128      def GetDefaultStroke(self):      def GetDefaultStroke(self):
129          return self.DefaultData.GetStroke()          return self.DefaultGroup.GetProperties().GetStroke()
130                    
131      def SetDefaultStrokeWidth(self, strokeWidth):      def SetDefaultStrokeWidth(self, strokeWidth):
132          self.DefaultData.SetStrokeWidth(strokeWidth)          assert(isinstance(strokeWidth, IntType))
133          self.layer.changed(LAYER_LEGEND_CHANGED, self.layer)          self.DefaultGroup.GetProperties().SetStrokeWidth(strokeWidth)
134            self.__SendMessage(LAYER_LEGEND_CHANGED)
135                    
136      def GetDefaultStrokeWidth(self):      def GetDefaultStrokeWidth(self):
137          return self.DefaultData.GetStrokeWidth()          return self.DefaultGroup.GetProperties().GetStrokeWidth()
138                    
139      def AddRange(self, min, max, data):      def AddGroup(self, item):
140          """Add a new range to the classification.          assert(isinstance(item, ClassGroup))
   
            A range allows a value to be classified if it falls between  
            min and max. Specifically, min <= value < max  
           
            min -- the lower bound.  
   
            max -- the upper bound.  
   
            data -- data that the value maps to. See class description.  
         """  
   
         if min >= max:  
             raise ValueError(_("Range minimum >= maximum!"))  
         self.ranges.append([min, max, data])  
         self.layer.changed(LAYER_LEGEND_CHANGED, self.layer)  
   
     def AddPoint(self, value, data):  
         """Associate a single value with data.  
141    
142             When this value is to be classified data will be returned.          if isinstance(item, ClassGroupDefault):
143                self.SetDefaultGroup(item)
144            elif isinstance(item, ClassGroupSingleton):
145                self.points.append(item)
146            elif isinstance(item, ClassGroupRange):
147                self.ranges.append(item)
148            elif isinstance(item, ClassGroupMap):
149                self.maps.append(item)
150            else:
151                raise ValueError(_("Unrecognized ClassGroup"))
152    
153             value -- classification value.          self.__SendMessage(LAYER_LEGEND_CHANGED)
154    
155             data  -- data that the value maps to. See class description.      def GetGroup(self, value):
         """  
   
         self.points[value] = data  
         self.layer.changed(LAYER_LEGEND_CHANGED, self.layer)  
   
     def GetProperties(self, value):  
156          """Return the associated data, or the default data.          """Return the associated data, or the default data.
157    
158             The following search technique is used:             The following search technique is used:
# Line 140  class Classification: Line 163  class Classification:
163                     the classification.                     the classification.
164    
165             value -- the value to classify. If there is no mapping,             value -- the value to classify. If there is no mapping,
166                      or value is None, return the default data                      or value is None, return the default properties
                     (which may be None)  
167          """          """
168    
169          if self.field is not None and value is not None:          if self.field is not None and value is not None:
             #  
             # first check the discrete values  
             #  
             if self.points.has_key(value):  
                 return self.points[value]  
   
             #  
             # now check the ranges  
             #  
             for p in self.ranges:  
                 if (p[RANGE_MIN] <= value) and (value < p[RANGE_MAX]):  
                     return p[RANGE_DATA]  
170    
171                for p in self:
172                    if p.Matches(value):
173                        return p
174    #           #
175    #           # check the discrete values
176    #           #
177    #           if self.points.has_key(value):
178    #               return self.points[value]
179    #           #for p in self.points:
180    #               #if p.Value
181    
182    #           #
183    #           # check the ranges
184    #           #
185    #           for p in self.ranges:
186    #               if p.InRange(value):
187    #                   return p
188    
189    #           #
190    #           # check the maps
191    #           #
192    #           for p in self.maps:
193    #               try:
194    #                   return p.Map(value)
195    #               except: pass
196    
197          return self.DefaultData          return self.DefaultGroup
198    
199        def GetProperties(self, value):
200            return self.GetGroup(value).GetProperties()
201    
202      def TreeInfo(self):      def TreeInfo(self):
203          items = []          items = []
204    
205          #          def build_color_item(text, color):
206          # XXX: shouldn't print anything if there are no classifications              if color is Color.None:
207          #                  return ("%s: %s" % (text, _("None")), None)
208    
209                        return ("%s: (%.3f, %.3f, %.3f)" %
210          def color_string(color):                      (text, color.red, color.green, color.blue),
211              if color is None:                      color)
212                  return "None"  
213              return "(%.3f, %.3f, %.3f)" % (color.red, color.green, color.blue)          def build_item(group, string):
214                label = group.GetLabel()
215                if label == "":
216                    label = string
217                else:
218                    label += " (%s)" % string
219    
220          def build_item(data):              props = group.GetProperties()
221              i = []              i = []
222                v = props.GetStroke()
223                i.append(build_color_item(_("Stroke"), v))
224                v = props.GetStrokeWidth()
225                i.append(_("Stroke Width: %s") % v)
226                v = props.GetFill()
227                i.append(build_color_item(_("Fill"), v))
228                return (label, i)
229    
230            for p in self:
231                if isinstance(p, ClassGroupDefault):
232                    items.append(build_item(self.DefaultGroup, _("'DEFAULT'")))
233                elif isinstance(p, ClassGroupSingleton):
234                    items.append(build_item(p, str(p.GetValue())))
235                elif isinstance(p, ClassGroupRange):
236                    items.append(build_item(p, "%s - %s" %
237                                               (p.GetMin(), p.GetMax())))
238    
239    #       for p in self.points.values():
240    #           items.append(build_item(p, str(p.GetValue())))
241    
242              v = data.GetStroke()  #       for p in self.ranges:
243              i.append((_("Stroke: %s") % color_string(v), v))  #           items.append(build_item(p, "%s - %s" % (p.GetMin(), p.GetMax())))
             v = data.GetStrokeWidth()  
             i.append((_("Stroke Width: %s") % v))  
             v = data.GetFill()  
             i.append((_("Fill: %s") % color_string(v), v))  
             return i  
   
         items.append((_("'DEFAULT'"), build_item(self.DefaultData)))  
   
         for name, data in self.points.items():  
             items.append((_("%s") % name, build_item(data)))  
   
         for p in self.ranges:  
             data = p[RANGE_DATA]  
             items.append((_("%s-%s") % (p[RANGE_MIN], p[RANGE_MAX])),  
                          build_item(data))  
244    
245          return (_("Classifications"), items)          return (_("Classification"), items)
246    
247    class ClassIterator:
248    
249  class ClassData:      def __init__(self, default, points, ranges, maps):
250            self.data = [default, points, ranges, maps]
251            self.data_iter = iter(self.data)
252            self.iter = None
253    
254        def __iter__(self):
255            return self
256    
257        def next(self):
258            if self.iter is None:
259                try:
260                    self.data_item = self.data_iter.next()
261                    self.iter = iter(self.data_item)
262                except TypeError:
263                    return self.data_item
264    
265            try:
266                return self.iter.next()
267            except StopIteration:
268                self.iter = None
269                return self.next()
270          
271    class ClassGroupProperties:
272    
273        def __init__(self, prop = None):
274    
275            if prop is not None:
276                self.SetStroke(prop.GetStroke())
277                self.SetStrokeWidth(prop.GetStrokeWidth())
278                self.SetFill(prop.GetFill())
279            else:
280                self.SetStroke(Color.None)
281                self.SetStrokeWidth(1)
282                self.SetFill(Color.None)
283    
     def __init__(self):  
         self.stroke = None  
         self.stroke_width = 0  
         self.fill = None  
         self.label = ""  
       
284      def GetStroke(self):      def GetStroke(self):
285          return self.stroke          return self.stroke
286    
287      def SetStroke(self, stroke):      def SetStroke(self, stroke):
288            assert(isinstance(stroke, Color))
289          self.stroke = stroke          self.stroke = stroke
290    
291      def GetStrokeWidth(self):      def GetStrokeWidth(self):
292          return self.stroke_width          return self.stroke_width
293    
294      def SetStrokeWidth(self, stroke_width):      def SetStrokeWidth(self, stroke_width):
295            assert(isinstance(stroke_width, IntType))
296            if (stroke_width < 1):
297                raise ValueError(_("stroke_width < 1"))
298    
299          self.stroke_width = stroke_width          self.stroke_width = stroke_width
300    
301      def GetFill(self):      def GetFill(self):
302          return self.fill          return self.fill
303    
304      def SetFill(self, fill):      def SetFill(self, fill):
305            assert(isinstance(fill, Color))
306          self.fill = fill          self.fill = fill
307    
308    
309    class ClassGroup:
310    
311        def __init__(self, label = ""):
312            self.label = label
313    
314      def GetLabel(self):      def GetLabel(self):
315          return self.label          return self.label
316    
317      def SetLabel(self, label):      def SetLabel(self, label):
318          self.label = label          self.label = label
319    
320        def Matches(self, value):
321            """This needs to be implemented by all subclasses."""
322            pass
323    
324        def GetProperties(self, value):
325            """This needs to be implemented by all subclasses."""
326            pass
327    
328        
329    class ClassGroupSingleton(ClassGroup):
330    
331        def __init__(self, value = 0, prop = None, label = ""):
332            ClassGroup.__init__(self, label)
333    
334            self.SetValue(value)
335            self.SetProperties(prop)
336    
337        def __copy__(self):
338            return ClassGroupSingleton(self.value, self.prop, self.label)
339    
340        def GetValue(self):
341            return self.value
342    
343        def SetValue(self, value):
344            self.value = value
345    
346        def Matches(self, value):
347            return self.value == value
348    
349        def GetProperties(self, value = None):
350            if value is None: return self.prop
351    
352            if self.Matches(value):
353                return self.prop
354            else:
355                return None
356    
357        def SetProperties(self, prop):
358            if prop is None: prop = ClassGroupProperties()
359            assert(isinstance(prop, ClassGroupProperties))
360            self.prop = prop
361    
362    
363    class ClassGroupDefault(ClassGroupSingleton):
364        def __init__(self, prop = None, label = ""):
365            ClassGroupSingleton.__init__(self, 0, prop, label)
366    
367        def __copy__(self):
368            return ClassGroupDefault(self.prop, self.label)
369    
370        def GetProperties(self, value = None):
371            return self.prop
372    
373    class ClassGroupRange(ClassGroup):
374    
375        def __init__(self, min = 0, max = 1, prop = None, label = ""):
376            ClassGroup.__init__(self, label)
377    
378            self.SetRange(min, max)
379            self.SetProperties(prop)
380    
381        def __copy__(self):
382            return ClassGroupRange(self.min, self.max, self.prop, self.label)
383    
384        def GetMin(self):
385            return self.min
386    
387        def SetMin(self, min):
388            self.SetRange(min, self.max)
389    
390        def GetMax(self):
391            return self.max
392    
393        def SetMax(self, max):
394            self.SetRange(self.min, max)
395    
396        def SetRange(self, min, max):
397            if min >= max:
398                raise ValueError(_("ClassGroupRange: %i(min) >= %i(max)!") %
399                                 (min, max))
400            self.min = min
401            self.max = max
402    
403        def GetRange(self):
404            return (self.min, self.max)
405    
406        def Matches(self, value):
407            return self.min <= value < self.max
408    
409        def GetProperties(self, value = None):
410            if value is None: return self.prop
411    
412            if self.Matches(value):
413                return self.prop
414            else:
415                return None
416    
417        def SetProperties(self, prop):
418            if prop is None: prop = ClassGroupProperties()
419            assert(isinstance(prop, ClassGroupProperties))
420            self.prop = prop
421    
422    class ClassGroupMap(ClassGroup):
423    
424        FUNC_ID = "id"
425    
426        def __init__(self, map_type = FUNC_ID, func = None, prop = None, label=""):
427            ClassGroup.__init__(self, prop)
428    
429            self.map_type = map_type
430            self.func = func
431    
432            if self.func is None:
433                self.func = func_id
434    
435        def Map(self, value):
436            return self.func(value)
437    
438        #
439        # built-in mappings
440        #
441        def func_id(value):
442            return value
443    

Legend:
Removed from v.397  
changed lines
  Added in v.449

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26