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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26