/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/classgen.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/Model/classgen.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 900 by jonathan, Wed May 14 11:15:41 2003 UTC revision 1157 by jonathan, Thu Jun 12 12:39:54 2003 UTC
# Line 5  Line 5 
5  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
6  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
7    
8    """
9    Functions to generate Classifications
10    """
11    
12    __version__ = "$Revision$"
13    # $Source$
14    # $Id$
15    
16  import operator  import operator
17    
18  from color import Color  from color import Color
# Line 12  from range import Range Line 20  from range import Range
20  from classification import Classification, ClassGroupSingleton, \  from classification import Classification, ClassGroupSingleton, \
21      ClassGroupRange, ClassGroupProperties      ClassGroupRange, ClassGroupProperties
22    
23  class ClassGenerator:  def generate_singletons(_list, numGroups, ramp):
24        """Generate a new classification consisting solely of singletons.
25    
26      def GenSingletonsFromList(self, _list, numGroups, ramp):      The resulting classification will consist of at most 'numGroups'
27          """Generate a new classification consisting solely of singletons.      groups whose group properties ramp between 'prop1' and 'prop2'. There
28        could be fewer groups if '_list' contains fewer that 'numGroups' items.
29    
30          The resulting classification will consist of at most 'numGroups'      _list -- any object that implements the iterator interface
         groups whose group properties ramp between 'prop1' and 'prop2'. There  
         could be fewer groups if '_list' contains fewer that 'numGroups' items.  
31    
32          _list -- any object that implements the iterator interface      numGroups -- how many groups to generate. This can not be
33                     determined while the classification is being
34                     generated because the stepping values must
35                     be precalculated to ramp between prop1 and prop2.
36    
37          numGroups -- how many groups to generate. This can not be      ramp -- an object which implements the CustomRamp interface
38                       determined while the classification is being      """
                      generated because the stepping values must  
                      be precalculated to ramp between prop1 and prop2.  
39    
40          ramp -- an object which implements the CustomRamp interface      clazz = Classification()
41          """      if numGroups == 0: return clazz
42    
43          clazz = Classification()      ramp.SetNumGroups(numGroups)
         if numGroups == 0: return clazz  
44    
45          ramp.SetNumGroups(numGroups)      for value, prop in zip(_list, ramp):
46            clazz.AppendGroup(ClassGroupSingleton(value, prop))
47    
48          for value, prop in zip(_list, ramp):      return clazz
             clazz.AppendGroup(ClassGroupSingleton(value, prop))  
49    
50          return clazz  def generate_uniform_distribution(min, max, numGroups, ramp, intStep = False):
51        """Generate a classification with numGroups range groups
52        each with the same interval.
53    
54      def GenSingletons(self, min, max, numGroups, ramp):      intStep -- force the calculated stepping to an integer.
55                   Useful if the values are integers but the
56                   number of groups specified doesn't evenly
57                   divide (max - min).
58        """
59    
60          clazz = Classification()      clazz = Classification()
61        if numGroups == 0: return clazz
62    
63          #step = int((max - min) / float(numGroups))      ramp.SetNumGroups(numGroups)
64    
65          if numGroups > 0:      cur_min = min
66    
67              step = int((max - min + 1) / float(numGroups))      i = 1
68              cur_value = min      end = "["
69        for prop in ramp:
70    
71              ramp.SetNumGroups(numGroups)          if intStep:
72                cur_max = min + int(round((i * (max - min + 1)) / float(numGroups)))
73            else:
74                cur_max = min + (i * (max - min)) / float(numGroups)
75    
76              for prop in ramp:          if i == numGroups:
77                  clazz.AppendGroup(ClassGroupSingleton(cur_value), prop)              cur_max = max
78                  cur_value += step              end = "]"
79    
80          return clazz          if cur_min == cur_max:
81                range = Range(("[", cur_min, cur_max, "]"))
82            else:
83                range = Range(("[", cur_min, cur_max, end))
84    
85      def GenUniformDistribution(self, min, max, numGroups,          clazz.AppendGroup(ClassGroupRange(range, None, prop))
                                ramp, intStep = False):  
         """Generate a classification with numGroups range groups  
         each with the same interval.  
86    
87          intStep -- force the calculated stepping to an integer.          cur_min = cur_max
88                     Useful if the values are integers but the          i += 1
                    number of groups specified doesn't evenly  
                    divide (max - min).  
         """  
89    
90          clazz = Classification()      return clazz
         if numGroups == 0: return clazz  
91    
         ramp.SetNumGroups(numGroups)  
92    
93          step = (max - min) / float(numGroups)  def generate_quantiles(_list, percents, ramp, _range):
94        """Generates a Classification which has groups of ranges that
95        represent quantiles of _list at the percentages given in percents.
96        Only the values that fall within _range are considered.
97    
98          if intStep:      Returns a tuple (adjusted, Classification) where adjusted is
99              step = int(step)      True if the Classification does not exactly represent the given
100        range, or if the Classification is empty.
101    
102          cur_min = min      _list -- a sort list of values
         cur_max = cur_min + step  
103    
104          i = 0      percents -- a sorted list of floats in the range 0.0-1.0 which
105          end = "["                  represent the upper bound of each quantile. the
106          for prop in ramp:                  union of all percentiles should be the entire
107                    range from 0.0-1.0
108    
109              if i == (numGroups - 1):      ramp -- an object which implements the CustomRamp interface
                 cur_max = max  
                 end = "]"  
110    
111        _range -- a Range object
112    
113              # this check guards against rounding issues      Raises a Value Error if 'percents' has fewer than two items, or
114              if cur_min != cur_max:      does not cover the entire range.
115                  range = Range("[" + str(float(cur_min)) + ";" +      """
                                     str(float(cur_max)) + end)  
                 clazz.AppendGroup(ClassGroupRange(range, None, prop))  
116    
117              cur_min = cur_max      clazz = Classification()
118              cur_max += step      quantiles = calculate_quantiles(_list, percents, _range)
119              i += 1      adjusted = True
120    
121          return clazz      if quantiles is not None:
122    
123            numGroups = len(quantiles[3])
124    
125      def GenQuantiles(self, _list, percents, ramp, _range):          if numGroups != 0:
         """Generates a Classification which has groups of ranges that  
         represent quantiles of _list at the percentages given in percents.  
         Only the values that fall within _range are considered.  
126    
127          Returns a tuple (adjusted, Classification) where adjusted is              adjusted = quantiles[0]
         True if the Classification does not exactly represent the given  
         range, or if the Classification is empty.  
128    
129          _list -- a sort list of values              ramp.SetNumGroups(numGroups)
130    
131          percents -- a sorted list of floats in the range 0.0-1.0 which              start, min, endMax, right = _range.GetRange()
                     represent the upper bound of each quantile  
132    
133          ramp -- an object which implements the CustomRamp interface              oldp = 0
134                i = 1
135                end = "]"
136    
137          _range -- a Range object              for (q, p), prop in zip(quantiles[3], ramp):
138          """                  if i == numGroups:
139                        max = endMax
140                        end = right
141                    else:
142                        max = _list[q]
143    
144          clazz = Classification()                  group = ClassGroupRange(Range((start, min, max, end)),
145          quantiles = self.CalculateQuantiles(_list, percents, _range)                                          None, prop)
146          adjusted = True      
147                    group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),
148                                                    round(p*100, 2)))
149                    oldp = p
150                    start = "]"
151                    min = max
152                    clazz.AppendGroup(group)
153                    i += 1
154    
155          if quantiles is not None:      return (adjusted, clazz)
156    
157              numGroups = len(quantiles[3])  def GenQuantiles0(_list, percents, ramp, _range):
158        """Same as GenQuantiles, but the first class won't be added to
159        the classification.
160    
161              if numGroups != 0:      Returns a tuple (adjusted, Classification, upper_class0) where
162        upper_class0 is the highest value inside the first class.
163    
164                  adjusted = quantiles[0]      _list -- a sort list of values
165    
166                  ramp.SetNumGroups(numGroups)      percents -- a sorted list of floats in the range 0.0-1.0 which
167                    represent the upper bound of each quantile. the
168                    union of all percentiles should be the entire
169                    range from 0.0-1.0
170    
171                  start, min, endMax, right = _range.GetRange()      ramp -- an object which implements the CustomRamp interface
172    
173                  if str(min) == '-inf':      _range -- a Range object
                     min = "-oo"  
                 elif str(min) == 'inf':  
                     min = "oo"  
174    
175                  if str(endMax) == '-inf':      Raises a Value Error if 'percents' has fewer than two items, or
176                      endMax = "-oo"      does not cover the entire range.
177                  elif str(endMax) == 'inf':      """
                     endMax = "oo"  
178    
179                  oldp = 0      clazz = Classification()
180                  i = 1      quantiles = calculate_quantiles(_list, percents, _range)
181                  end = "]"      adjusted = True
182    
183                  for (q, p), prop in zip(quantiles[3], ramp):      if quantiles is not None:
                     if i == numGroups:  
                         max = endMax  
                         end = right  
                     else:  
                         max = _list[q]  
184    
185                      group = ClassGroupRange(Range(start + str(min) + ";" +          numGroups = len(quantiles[3]) - 1
                                                           str(max) + end),  
                                             None, prop)  
           
                     group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),  
                                                     round(p*100, 2)))  
                     oldp = p  
                     start = "]"  
                     min = max  
                     clazz.AppendGroup(group)  
                     i += 1  
   
         return (adjusted, clazz)  
   
     def CalculateQuantiles(self, _list, percents, _range):  
         """Calculate quantiles for the given _list of percents from the  
         sorted list of values that are in range.  
                                                                                   
         This may not actually generate len(percents) quantiles if  
         many of the values that fall on quantile borders are the same.  
   
         Returns a tuple of the form:  
             (adjusted, minIndex, maxIndex, [quantile_list])  
   
         where adjusted is True if the the quantile percentages differ from  
         those supplied, minIndex is the index into _list where the  
         minimum value used is located, maxIndex is the index into _list  
         where the maximum value used is located, and quantile_list is a  
         list of tuples of the form: (list_index, quantile_percentage)  
   
         Returns None, if no quantiles could be generated based on the  
         given range or input list.  
186    
187          _list -- a sort list of values          if numGroups > 0:
188                adjusted = quantiles[0]
189    
190          percents -- a sorted list of floats in the range 0.0-1.0 which              ramp.SetNumGroups(numGroups)
                     represent the upper bound of each quantile  
191    
192          _range -- a Range object              start, min, endMax, right = _range.GetRange()
193          """  
194                    class0 = quantiles[3][0]
195          quantiles = []              min = _list[class0[0]]
196          adjusted = False              oldp = class0[1]
197                i = 1
198                end = "]"
199    
200                for (q, p), prop in zip(quantiles[3][1:], ramp):
201                    if i == numGroups:
202                        max = endMax
203                        end = right
204                    else:
205                        max = _list[q]
206    
207                    group = ClassGroupRange(Range((start, min, max, end)),
208                                            None, prop)
209        
210                    group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),
211                                                    round(p*100, 2)))
212                    oldp = p
213                    start = "]"
214                    min = max
215                    clazz.AppendGroup(group)
216                    i += 1
217    
218        return (adjusted, clazz, _list[class0[0]])
219    
220    
221    def calculate_quantiles(_list, percents, _range):
222        """Calculate quantiles for the given _list of percents from the
223        sorted list of values that are in range.
224                                                                                
225        This may not actually generate len(percents) quantiles if
226        many of the values that fall on quantile borders are the same.
227    
228        Returns a tuple of the form:
229            (adjusted, minIndex, maxIndex, [quantile_list])
230    
231        where adjusted is True if the the quantile percentages differ from
232        those supplied, minIndex is the index into _list where the
233        minimum value used is located, maxIndex is the index into _list
234        where the maximum value used is located, and quantile_list is a
235        list of tuples of the form: (list_index, quantile_percentage)
236    
237        Returns None, if no quantiles could be generated based on the
238        given range or input list.
239    
240        _list -- a sort list of values
241    
242        percents -- a sorted list of floats in the range 0.0-1.0 which
243                    represent the upper bound of each quantile. the
244                    union of all percentiles should be the entire
245                    range from 0.0-1.0
246    
247        _range -- a Range object
248    
249        Raises a Value Error if 'percents' has fewer than two items, or
250        does not cover the entire range.
251        """
252    
253        quantiles = []
254        adjusted = False
255    
256        if len(percents) <= 1:
257            raise ValueError("percents parameter must have more than one item")
258    
259        if percents[len(percents) - 1] != 1.0:
260            raise ValueError("percents does not cover the entire range")
261    
262        #
263        # find what part of the _list range covers
264        #
265        minIndex = -1
266        maxIndex = -2
267        for i in xrange(0, len(_list), 1):
268            if operator.contains(_range, _list[i]):
269                minIndex = i
270                break
271    
272        for i in xrange(len(_list)-1, -1, -1):
273            if operator.contains(_range, _list[i]):
274                maxIndex = i
275                break
276    
277        numValues = maxIndex - minIndex + 1
278    
279        if numValues > 0:
280    
281            #
282            # build a list of unique indices into list of where each
283            # quantile *should* be. set adjusted if the resulting
284            # indices are different
285            #
286            quantiles = {}
287            for p in percents:
288                index = min(minIndex + int(p*numValues)-1, maxIndex)
289    
290                adjusted = adjusted \
291                    or quantiles.has_key(index) \
292                    or ((index - minIndex + 1) / float(numValues)) != p
293    
294                quantiles[index] = 0
295    
296            quantiles = quantiles.keys()
297            quantiles.sort()
298    
299            #
300            # the current quantile index must be strictly greater than
301            # the lowerBound
302            #
303            lowerBound = minIndex - 1
304    
305            for qindex in xrange(len(quantiles)):
306                if lowerBound >= maxIndex:
307                    # discard higher quantiles
308                    quantiles = quantiles[:qindex]
309                    break
310    
311                # lowerBound + 1 is always a valid index
312    
         if len(percents) != 0:  
                                                                                 
313              #              #
314              # find what part of the _list range covers              # bump up the current quantile index to be a usable index
315                # if it currently falls below the lowerBound
316              #              #
317              minIndex = -1              if quantiles[qindex] <= lowerBound:
318              maxIndex = -2                  quantiles[qindex] = lowerBound + 1
319              for i in xrange(0, len(_list), 1):  
320                  if operator.contains(_range, _list[i]):              listIndex = quantiles[qindex]
321                      minIndex = i              value = _list[listIndex]
322                      break  
323                #
324              for i in xrange(len(_list)-1, -1, -1):              # look for similar values around the quantile index
325                  if operator.contains(_range, _list[i]):              #
326                      maxIndex = i              lindex = listIndex - 1
327                      break              while lindex > lowerBound and value == _list[lindex]:
328                    lindex -= 1
329              numValues = maxIndex - minIndex + 1              lcount = (listIndex - 1) - lindex
330    
331              if numValues > 0:              rindex = listIndex + 1
332                while rindex < maxIndex + 1 and value == _list[rindex]:
333                  #                  rindex += 1
334                  # build a list of unique indices into list of where each              rcount = (listIndex + 1) - rindex
335                  # quantile *should* be. set adjusted if the resulting  
336                  # indices are different              #
337                  #              # adjust the current quantile index based on how many
338                  quantiles = {}              # numbers in the _list are the same as the current value
339                  for p in percents:              #
340                      index = min(minIndex + int(p*numValues)-1, maxIndex)              newIndex = listIndex
341                if lcount == rcount:
342                      adjusted = adjusted \                  if lcount != 0:
                         or quantiles.has_key(index) \  
                         or ((index - minIndex + 1) / float(numValues)) != p  
   
                     quantiles[index] = 0  
   
                 quantiles = quantiles.keys()  
                 quantiles.sort()  
   
                 #  
                 # the current quantile index must be strictly greater than  
                 # the lowerBound  
                 #  
                 lowerBound = minIndex - 1  
       
                 for qindex in xrange(len(quantiles)):  
                     if lowerBound >= maxIndex:  
                         # discard higher quantiles  
                         quantiles = quantiles[:qindex]  
                         break  
       
                     # lowerBound + 1 is always a valid index  
       
                     #  
                     # bump up the current quantile index to be a usable index  
                     # if it currently falls below the lowerBound  
                     #  
                     if quantiles[qindex] <= lowerBound:  
                         quantiles[qindex] = lowerBound + 1  
           
                     listIndex = quantiles[qindex]  
                     value = _list[listIndex]  
       
                     #  
                     # look for similar values around the quantile index  
                     #  
                     lindex = listIndex - 1  
                     while lindex > lowerBound and value == _list[lindex]:  
                         lindex -= 1  
                     lcount = (listIndex - 1) - lindex  
       
                     rindex = listIndex + 1  
                     while rindex < maxIndex + 1 and value == _list[rindex]:  
                         rindex += 1  
                     rcount = (listIndex + 1) - rindex  
       
343                      #                      #
344                      # adjust the current quantile index based on how many                      # there are an equal number of numbers to the left
345                      # numbers in the _list are the same as the current value                      # and right, try going to the left first unless
346                        # doing so creates an empty quantile.
347                      #                      #
348                      newIndex = listIndex                      if lindex != lowerBound:
349                      if lcount == rcount:                          newIndex = lindex
350                          if lcount != 0:                      else:
                             #  
                             # there are an equal number of numbers to the left  
                             # and right, try going to the left first unless  
                             # doing so creates an empty quantile.  
                             #  
                             if lindex != lowerBound:  
                                 newIndex = lindex  
                             else:  
                                 newIndex = rindex - 1  
       
                     elif lcount < rcount:  
                         # there are fewer items to the left, so  
                         # try going to the left first unless  
                         # doing so creates an empty quantile.  
                         if lindex != lowerBound:  
                             newIndex = lindex  
                         else:  
                             newIndex = rindex - 1  
       
                     elif rcount < lcount:  
                         # there are fewer items to the right, so go to the right  
351                          newIndex = rindex - 1                          newIndex = rindex - 1
       
                     adjusted = adjusted or newIndex != listIndex  
352    
353                      quantiles[qindex] = newIndex              elif lcount < rcount:
354                      lowerBound = quantiles[qindex]                  # there are fewer items to the left, so
355                        # try going to the left first unless
356          #                  # doing so creates an empty quantile.
357          # since quantiles is only set if the code is at least a little                  if lindex != lowerBound:
358          # successful, an empty list will be generated in the case that                      newIndex = lindex
359          # we fail to get to the real body of the algorithm                  else:
360          #                      newIndex = rindex - 1
361          if len(quantiles) == 0:  
362              return None              elif rcount < lcount:
363          else:                  # there are fewer items to the right, so go to the right
364              return (adjusted, minIndex, maxIndex,                  newIndex = rindex - 1
365                      [(q, (q - minIndex+1) / float(numValues)) \  
366                       for q in quantiles])              adjusted = adjusted or newIndex != listIndex
367    
368                quantiles[qindex] = newIndex
369                lowerBound = quantiles[qindex]
370    
371        if len(quantiles) == 0:
372            return None
373        else:
374            return (adjusted, minIndex, maxIndex,
375                    [(q, (q - minIndex+1) / float(numValues)) \
376                     for q in quantiles])
377    
378  CLR  = 0  CLR  = 0
379  STEP = 1  STEP = 1
# Line 433  class CustomRamp: Line 479  class CustomRamp:
479              color = [color1.red   * 255,              color = [color1.red   * 255,
480                       color1.green * 255,                       color1.green * 255,
481                       color1.blue  * 255]                       color1.blue  * 255]
482              step = ((color2.red   * 255 - color1.red   * 255)   / numGroups,              step = ((color2.red   * 255 - color1.red   * 255) / numGroups,
483                      (color2.green * 255 - color1.green * 255) / numGroups,                      (color2.green * 255 - color1.green * 255) / numGroups,
484                      (color2.blue  * 255 - color1.blue  * 255)  / numGroups)                      (color2.blue  * 255 - color1.blue  * 255) / numGroups)
485    
486    
487          return (color, step)          return (color, step)
# Line 515  class HotToColdRamp: Line 561  class HotToColdRamp:
561          prop.SetFill(Color(clr[0], clr[1], clr[2]))          prop.SetFill(Color(clr[0], clr[1], clr[2]))
562    
563          return prop          return prop
   
 #class Colors16Ramp:  
 #  
     #def __iter__(self):  
         #return self  
 #  
     #def GetRamp(self):  
         #return self  
 #  
     #def SetNumGroups(self, num):  
         #if num < 0:  
             #return False  
 #  
         #self.index = 0  
 #  
         #return True  

Legend:
Removed from v.900  
changed lines
  Added in v.1157

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26