/[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 381 by jonathan, Tue Jan 28 18:37:05 2003 UTC revision 410 by jonathan, Wed Feb 19 16:51:12 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 getProperties() 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  from Thuban.Model.color import Color
31    
# Line 32  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.layer = layer
51          self.points = {}          self.points = {}
52          self.ranges = []          self.ranges = []
53          self.setField(field)          self.maps   = []
54          self.setNull(None)          self.DefaultData = ClassDataDefault()
55                    self.field = field
56      def setField(self, field):          #self.SetField(field)
57    
58        def SendMessage(self, message):
59            if self.layer is not None:
60                self.layer.changed(message, self.layer)
61        
62        def SetField(self, field):
63          """Set the name of the data table field to use.          """Set the name of the data table field to use.
64                    
65             field -- if None then all values map to NullData             field -- if None then all values map to the default data
66          """          """
67    
68          self.field = field          self.field = field
69            self.SendMessage(LAYER_LEGEND_CHANGED)
70    
71      def setNull(self, data):      def GetField(self):
72          """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.  
         """  
   
         self.NullData = data  
73    
74      def addRange(self, min, max, data):      def SetLayer(self, layer):
75          """Add a new range to the classification.          self.layer = layer
76            self.SendMessage(LAYER_LEGEND_CHANGED)
77    
78             A range allows a value to be classified if it falls between      def GetLayer(self):
79             min and max. Specifically, min <= value < max          return layer.self
           
            min -- the lower bound.  
80    
81             max -- the upper bound.      def SetDefaultData(self, data):
82            """Set the data to be used when a value can't be classified.
83    
84             data -- data that the value maps to. See class description.             data -- data that the value maps to. See class description.
85          """          """
86    
87          if min >= max:          self.DefaultData = data
             raise ValueError(_("Range minimum >= maximum!"))  
         self.ranges.append([min, max, data])  
   
     def addPoint(self, value, data):  
         """Associate a single value with data.  
88    
89             When this value is to be classified data will be returned.      def GetDefaultData(self):
90            return self.DefaultData
91    
92             value -- classification value.      def SetDefaultFill(self, fill):
93            self.DefaultData.SetFill(fill)
94            self.SendMessage(LAYER_LEGEND_CHANGED)
95            
96        def GetDefaultFill(self):
97            return self.DefaultData.GetFill()
98            
99        def SetDefaultStroke(self, stroke):
100            self.DefaultData.SetStroke(stroke)
101            self.SendMessage(LAYER_LEGEND_CHANGED)
102            
103        def GetDefaultStroke(self):
104            return self.DefaultData.GetStroke()
105            
106        def SetDefaultStrokeWidth(self, strokeWidth):
107            self.DefaultData.SetStrokeWidth(strokeWidth)
108            self.SendMessage(LAYER_LEGEND_CHANGED)
109            
110        def GetDefaultStrokeWidth(self):
111            return self.DefaultData.GetStrokeWidth()
112            
113        def AddClassData(self, item):
114            type = item.GetType()
115    
116             data  -- data that the value maps to. See class description.          if type == ClassData.POINT:
117          """              self.points[item.GetValue()] = item
118            elif type == ClassData.RANGE:
119                self.ranges.append(item)
120            elif type == ClassData.MAP:
121                self.maps.append(item)
122            elif type == ClassData.DEFAULT:
123                self.DefaultData = item
124            else:
125                raise ValueError(_("Unrecognized ClassData type %s") % type)
126    
127          self.points[value] = data          self.SendMessage(LAYER_LEGEND_CHANGED)
128    
129      def getProperties(self, value):      def GetProperties(self, value):
130          """Return the associated data, or the NullData.          """Return the associated data, or the default data.
131    
132             The following search technique is used:             The following search technique is used:
133                 (1) if the field is None, return NullData                 (1) if the field is None, return the default data
134                 (2) check if the value exists as a single value                 (2) check if the value exists as a single value
135                 (3) check if the value falls within a range. Ranges                 (3) check if the value falls within a range. Ranges
136                     are checked in the order they were added to                     are checked in the order they were added to
137                     the classification.                     the classification.
138    
139             value -- the value to classify. If there is no mapping             value -- the value to classify. If there is no mapping,
140                      return the NullData (which may be None)                      or value is None, return the default data
141                        (which may be None)
142          """          """
143    
144          if self.field is not None:          if self.field is not None and value is not None:
145              #              #
146              # first check the discrete values              # check the discrete values
147              #              #
148              if self.points.has_key(value):              if self.points.has_key(value):
149                  return self.points[value]                  return self.points[value]
150    
151              #              #
152              # now check the ranges              # check the ranges
153              #              #
154              for p in self.ranges:              for p in self.ranges:
155                  if (p[RANGE_MIN] <= value) and (value < p[RANGE_MAX]):                  if p.InRange(value):
156                      return p[RANGE_DATA]                      return p
157    
158                #
159                # check the maps
160                #
161                for p in self.maps:
162                    try:
163                        return p.Map(value)
164                    except: pass
165    
166          return self.NullData          return self.DefaultData
167    
168      def TreeInfo(self):      def TreeInfo(self):
169          items = []          items = []
170    
171          #          def build_color_item(text, color):
172          # shouldn't print anything if there are no classifications              if color is Color.None:
173          #                  return ("%s: %s" % (text, _("None")), None)
174    
175                        return ("%s: (%.3f, %.3f, %.3f)" %
176          def color_string(color):                      (text, color.red, color.green, color.blue),
177              if color is None:                      color)
178                  return "None"  
179              return "(%.3f, %.3f, %.3f)" % (color.red, color.green, color.blue)          def build_item(data, string):
180                label = data.GetLabel()
181                if label == "":
182                    label = string
183                else:
184                    label += " (%s)" % string
185    
         if self.NullData is not None:  
186              i = []              i = []
187              for key, value in self.NullData.items():              v = data.GetStroke()
188                  if isinstance(value, Color):              i.append(build_color_item(_("Stroke"), v))
189                      i.append((_("%s: %s") % (key, color_string(value)), value))              v = data.GetStrokeWidth()
190                  else:              i.append(_("Stroke Width: %s") % v)
191                      i.append(_("%s: %s") % (key, value))              v = data.GetFill()
192              items.append((_("'NULL'"), i))              i.append(build_color_item(_("Fill"), v))
193                return (label, i)
194    
195          for name, data in self.points.items():          items.append(build_item(self.DefaultData, _("'DEFAULT'")))
196              i = []  
197              for key, value in data.items():          for p in self.points.values():
198                  if isinstance(value, Color):              items.append(build_item(p, str(p.GetValue())))
                     i.append((_("%s: %s") % (key, color_string(value)), value))  
                 else:  
                     i.append(_("%s: %s") % (key, value))  
             items.append((_("%s") % name, i))  
199    
200          for p in self.ranges:          for p in self.ranges:
201              i = []              items.append(build_item(p, "%s - %s" % (p.GetMin(), p.GetMax())))
202              data = p[RANGE_DATA]  
             for key, value in data.items():  
                 if isinstance(value, Color):  
                     i.append((_("%s: %s") % (key, color_string(value)), value))  
                 else:  
                     i.append(_("%s: %s") % (key, value))  
             items.append((_("%s-%s") % (p[RANGE_MIN], p[RANGE_MAX], i)))  
         
203          return (_("Classifications"), items)          return (_("Classifications"), items)
204    
205    
206    class ClassData:
207    
208        INVALID = -1
209        DEFAULT = 0
210        POINT = 1
211        RANGE = 2
212        MAP   = 3
213    
214        def __init__(self, type = INVALID, classData = None):
215    
216            if classData is not None:
217                self.SetStroke(classData.GetStroke())
218                self.SetStrokeWidth(classData.GetStrokeWidth())
219                self.SetFill(classData.GetFill())
220            else:
221                self.SetStroke(Color.None)
222                self.SetStrokeWidth(1)
223                self.SetFill(Color.None)
224    
225            self.type = type
226            self.label = ""
227        
228        def GetType(self):
229            return self.type
230    
231        def GetStroke(self):
232            return self.stroke
233    
234        def SetStroke(self, stroke):
235            assert(isinstance(stroke, Color))
236            self.stroke = stroke
237    
238        def GetStrokeWidth(self):
239            return self.stroke_width
240    
241        def SetStrokeWidth(self, stroke_width):
242            if (stroke_width < 1):
243                raise ValueError(_("stroke_width < 1"))
244    
245            self.stroke_width = stroke_width
246    
247        def GetFill(self):
248            return self.fill
249    
250        def SetFill(self, fill):
251            assert(isinstance(fill, Color))
252            self.fill = fill
253    
254        def GetLabel(self):
255            return self.label
256    
257        def SetLabel(self, label):
258            self.label = label
259    
260    class ClassDataDefault(ClassData):
261        def __init__(self, classData = None):
262            ClassData.__init__(self, ClassData.DEFAULT, classData)
263        
264    class ClassDataPoint(ClassData):
265    
266        def __init__(self, value = 0, classData = None):
267            ClassData.__init__(self, ClassData.POINT, classData)
268    
269            self.value = value
270    
271        def GetValue(self):
272            return self.value
273    
274        def SetValue(self, value):
275            self.value = value
276    
277    class ClassDataRange(ClassData):
278    
279        def __init__(self, min = 0, max = 1, classData = None):
280            ClassData.__init__(self, ClassData.RANGE, classData)
281    
282            if min >= max:
283                raise ValueError(_("ClassDataRange: %i(min) >= %i(max)!") %
284                                 (min, max))
285    
286            self.SetRange(min, max)
287    
288        def GetMin(self):
289            return self.min
290    
291        def SetMin(self, min):
292            self.SetRange(min, self.max)
293    
294        def GetMax(self):
295            return self.max
296    
297        def SetMax(self, max):
298            self.SetRange(self.min, max)
299    
300        def SetRange(self, min, max):
301            self.min = min
302            self.max = max
303            if min >= max:
304                raise ValueError(_("ClassDataRange: %i(min) >= %i(max)!") %
305                                 (min, max))
306    
307        def GetRange(self):
308            return (self.min, self.max)
309    
310        def InRange(self, value):
311            return self.min <= value < self.max
312    
313    class ClassDataMap(ClassData):
314    
315        FUNC_ID = "id"
316    
317        def __init__(self, map_type = FUNC_ID, func = None, classData = None):
318            ClassData.__init__(self, ClassData.MAP, classData)
319    
320            self.map_type = map_type
321            self.func = func
322    
323            if self.func is None:
324                self.func = func_id
325    
326        def Map(self, value):
327            return self.func(value)
328    
329        #
330        # built-in mappings
331        #
332        def func_id(value):
333            return value
334    

Legend:
Removed from v.381  
changed lines
  Added in v.410

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26