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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1098 - (hide annotations)
Fri May 30 06:27:44 2003 UTC (21 years, 9 months ago) by jonathan
Original Path: trunk/thuban/Thuban/Model/classgen.py
File MIME type: text/x-python
File size: 15606 byte(s)
Remove ClassGenerator class but make
        all the methods functions. Fixes RTBug #1903.

1 jonathan 886 # Copyright (c) 2003 by Intevation GmbH
2     # Authors:
3     # Jonathan Coles <[email protected]>
4     #
5     # This program is free software under the GPL (>=v2)
6     # Read the file COPYING coming with Thuban for details.
7    
8 tkoester 986 """
9 jonathan 1098 Functions to generate Classifications
10 tkoester 986 """
11    
12     __version__ = "$Revision$"
13     # $Source$
14     # $Id$
15    
16 jonathan 886 import operator
17    
18     from color import Color
19     from range import Range
20     from classification import Classification, ClassGroupSingleton, \
21     ClassGroupRange, ClassGroupProperties
22    
23 jonathan 1098 def GenSingletonsFromList(_list, numGroups, ramp):
24     """Generate a new classification consisting solely of singletons.
25 jonathan 886
26 jonathan 1098 The resulting classification will consist of at most 'numGroups'
27     groups whose group properties ramp between 'prop1' and 'prop2'. There
28     could be fewer groups if '_list' contains fewer that 'numGroups' items.
29 jonathan 886
30 jonathan 1098 _list -- any object that implements the iterator interface
31 jonathan 886
32 jonathan 1098 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 jonathan 886
37 jonathan 1098 ramp -- an object which implements the CustomRamp interface
38     """
39 jonathan 886
40 jonathan 1098 clazz = Classification()
41     if numGroups == 0: return clazz
42 jonathan 886
43 jonathan 1098 ramp.SetNumGroups(numGroups)
44 jonathan 886
45 jonathan 1098 for value, prop in zip(_list, ramp):
46     clazz.AppendGroup(ClassGroupSingleton(value, prop))
47    
48     return clazz
49    
50     def GenSingletons(min, max, numGroups, ramp):
51    
52     clazz = Classification()
53    
54     #step = int((max - min) / float(numGroups))
55    
56     if numGroups > 0:
57    
58     step = int((max - min + 1) / float(numGroups))
59     cur_value = min
60    
61 jonathan 886 ramp.SetNumGroups(numGroups)
62    
63 jonathan 1098 for prop in ramp:
64     clazz.AppendGroup(ClassGroupSingleton(cur_value), prop)
65     cur_value += step
66 jonathan 886
67 jonathan 1098 return clazz
68 jonathan 886
69 jonathan 1098 def GenUniformDistribution(min, max, numGroups,
70     ramp, intStep = False):
71     """Generate a classification with numGroups range groups
72     each with the same interval.
73 jonathan 886
74 jonathan 1098 intStep -- force the calculated stepping to an integer.
75     Useful if the values are integers but the
76     number of groups specified doesn't evenly
77     divide (max - min).
78     """
79 jonathan 886
80 jonathan 1098 clazz = Classification()
81     if numGroups == 0: return clazz
82 jonathan 886
83 jonathan 1098 ramp.SetNumGroups(numGroups)
84 jonathan 886
85 jonathan 1098 step = (max - min) / float(numGroups)
86 jonathan 886
87 jonathan 1098 if intStep:
88     step = int(step)
89 jonathan 886
90 jonathan 1098 cur_min = min
91     cur_max = cur_min + step
92 jonathan 886
93 jonathan 1098 i = 0
94     end = "["
95     for prop in ramp:
96 jonathan 886
97 jonathan 1098 if i == (numGroups - 1):
98     cur_max = max
99     end = "]"
100 jonathan 886
101    
102 jonathan 1098 # this check guards against rounding issues
103     if cur_min != cur_max:
104     range = Range(("[", cur_min, cur_max, end))
105     clazz.AppendGroup(ClassGroupRange(range, None, prop))
106 jonathan 886
107 jonathan 1098 cur_min = cur_max
108     cur_max += step
109     i += 1
110 jonathan 886
111 jonathan 1098 return clazz
112 jonathan 886
113    
114 jonathan 1098 def GenQuantiles(_list, percents, ramp, _range):
115     """Generates a Classification which has groups of ranges that
116     represent quantiles of _list at the percentages given in percents.
117     Only the values that fall within _range are considered.
118 jonathan 886
119 jonathan 1098 Returns a tuple (adjusted, Classification) where adjusted is
120     True if the Classification does not exactly represent the given
121     range, or if the Classification is empty.
122 jonathan 886
123 jonathan 1098 _list -- a sort list of values
124 jonathan 886
125 jonathan 1098 percents -- a sorted list of floats in the range 0.0-1.0 which
126     represent the upper bound of each quantile
127 jonathan 886
128 jonathan 1098 ramp -- an object which implements the CustomRamp interface
129 jonathan 886
130 jonathan 1098 _range -- a Range object
131     """
132 jonathan 886
133 jonathan 1098 clazz = Classification()
134     quantiles = CalculateQuantiles(_list, percents, _range)
135     adjusted = True
136 jonathan 886
137 jonathan 1098 if quantiles is not None:
138 jonathan 886
139 jonathan 1098 numGroups = len(quantiles[3])
140 jonathan 895
141 jonathan 1098 if numGroups != 0:
142 jonathan 895
143 jonathan 1098 adjusted = quantiles[0]
144 jonathan 895
145 jonathan 1098 ramp.SetNumGroups(numGroups)
146 jonathan 895
147 jonathan 1098 start, min, endMax, right = _range.GetRange()
148 jonathan 895
149 jonathan 1098 oldp = 0
150     i = 1
151     end = "]"
152 jonathan 895
153 jonathan 1098 for (q, p), prop in zip(quantiles[3], ramp):
154     if i == numGroups:
155     max = endMax
156     end = right
157     else:
158     max = _list[q]
159 jonathan 886
160 jonathan 1098 group = ClassGroupRange(Range((start, min, max, end)),
161     None, prop)
162    
163     group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),
164     round(p*100, 2)))
165     oldp = p
166     start = "]"
167     min = max
168     clazz.AppendGroup(group)
169     i += 1
170 jonathan 886
171 jonathan 1098 return (adjusted, clazz)
172 jonathan 886
173 jonathan 1098 def CalculateQuantiles(_list, percents, _range):
174     """Calculate quantiles for the given _list of percents from the
175     sorted list of values that are in range.
176    
177     This may not actually generate len(percents) quantiles if
178     many of the values that fall on quantile borders are the same.
179 jonathan 886
180 jonathan 1098 Returns a tuple of the form:
181     (adjusted, minIndex, maxIndex, [quantile_list])
182 jonathan 886
183 jonathan 1098 where adjusted is True if the the quantile percentages differ from
184     those supplied, minIndex is the index into _list where the
185     minimum value used is located, maxIndex is the index into _list
186     where the maximum value used is located, and quantile_list is a
187     list of tuples of the form: (list_index, quantile_percentage)
188 jonathan 886
189 jonathan 1098 Returns None, if no quantiles could be generated based on the
190     given range or input list.
191 jonathan 900
192 jonathan 1098 _list -- a sort list of values
193 jonathan 900
194 jonathan 1098 percents -- a sorted list of floats in the range 0.0-1.0 which
195     represent the upper bound of each quantile
196 jonathan 900
197 jonathan 1098 _range -- a Range object
198     """
199 jonathan 895
200 jonathan 1098 quantiles = []
201     adjusted = False
202 jonathan 895
203 jonathan 1098 if len(percents) != 0:
204    
205     #
206     # find what part of the _list range covers
207     #
208     minIndex = -1
209     maxIndex = -2
210     for i in xrange(0, len(_list), 1):
211     if operator.contains(_range, _list[i]):
212     minIndex = i
213     break
214 jonathan 886
215 jonathan 1098 for i in xrange(len(_list)-1, -1, -1):
216     if operator.contains(_range, _list[i]):
217     maxIndex = i
218     break
219 jonathan 886
220 jonathan 1098 numValues = maxIndex - minIndex + 1
221 jonathan 895
222 jonathan 1098 if numValues > 0:
223 jonathan 895
224 jonathan 1098 #
225     # build a list of unique indices into list of where each
226     # quantile *should* be. set adjusted if the resulting
227     # indices are different
228     #
229     quantiles = {}
230     for p in percents:
231     index = min(minIndex + int(p*numValues)-1, maxIndex)
232 jonathan 895
233 jonathan 1098 adjusted = adjusted \
234     or quantiles.has_key(index) \
235     or ((index - minIndex + 1) / float(numValues)) != p
236 jonathan 895
237 jonathan 1098 quantiles[index] = 0
238 jonathan 895
239 jonathan 1098 quantiles = quantiles.keys()
240     quantiles.sort()
241    
242 jonathan 886 #
243 jonathan 1098 # the current quantile index must be strictly greater than
244     # the lowerBound
245 jonathan 886 #
246 jonathan 1098 lowerBound = minIndex - 1
247 jonathan 886
248 jonathan 1098 for qindex in xrange(len(quantiles)):
249     if lowerBound >= maxIndex:
250     # discard higher quantiles
251     quantiles = quantiles[:qindex]
252 jonathan 895 break
253 jonathan 886
254 jonathan 1098 # lowerBound + 1 is always a valid index
255 jonathan 886
256 jonathan 1098 #
257     # bump up the current quantile index to be a usable index
258     # if it currently falls below the lowerBound
259     #
260     if quantiles[qindex] <= lowerBound:
261     quantiles[qindex] = lowerBound + 1
262    
263     listIndex = quantiles[qindex]
264     value = _list[listIndex]
265 jonathan 895
266 jonathan 886 #
267 jonathan 1098 # look for similar values around the quantile index
268 jonathan 886 #
269 jonathan 1098 lindex = listIndex - 1
270     while lindex > lowerBound and value == _list[lindex]:
271     lindex -= 1
272     lcount = (listIndex - 1) - lindex
273 jonathan 886
274 jonathan 1098 rindex = listIndex + 1
275     while rindex < maxIndex + 1 and value == _list[rindex]:
276     rindex += 1
277     rcount = (listIndex + 1) - rindex
278 jonathan 886
279     #
280 jonathan 1098 # adjust the current quantile index based on how many
281     # numbers in the _list are the same as the current value
282 jonathan 886 #
283 jonathan 1098 newIndex = listIndex
284     if lcount == rcount:
285     if lcount != 0:
286     #
287     # there are an equal number of numbers to the left
288     # and right, try going to the left first unless
289 jonathan 886 # doing so creates an empty quantile.
290 jonathan 1098 #
291 jonathan 886 if lindex != lowerBound:
292     newIndex = lindex
293     else:
294     newIndex = rindex - 1
295 jonathan 1098
296     elif lcount < rcount:
297     # there are fewer items to the left, so
298     # try going to the left first unless
299     # doing so creates an empty quantile.
300     if lindex != lowerBound:
301     newIndex = lindex
302     else:
303 jonathan 886 newIndex = rindex - 1
304 jonathan 895
305 jonathan 1098 elif rcount < lcount:
306     # there are fewer items to the right, so go to the right
307     newIndex = rindex - 1
308 jonathan 886
309 jonathan 1098 adjusted = adjusted or newIndex != listIndex
310    
311     quantiles[qindex] = newIndex
312     lowerBound = quantiles[qindex]
313    
314     if len(quantiles) == 0:
315     return None
316     else:
317     return (adjusted, minIndex, maxIndex,
318     [(q, (q - minIndex+1) / float(numValues)) \
319     for q in quantiles])
320    
321 jonathan 886 CLR = 0
322     STEP = 1
323     class CustomRamp:
324    
325     def __init__(self, prop1, prop2):
326     self.prop1 = prop1
327     self.prop2 = prop2
328    
329     self.count = 0
330    
331     def __iter__(self):
332     return self
333    
334     def GetRamp(self):
335     return self
336    
337     def SetNumGroups(self, num):
338    
339     if num <= 0:
340     return False
341    
342     self.count = int(num)
343     num = float(num)
344    
345     prop1 = self.prop1
346     prop2 = self.prop2
347    
348     clr = prop1.GetLineColor()
349     lineColor2 = prop2.GetLineColor()
350    
351     self.noLine = clr is not Color.Transparent \
352     and lineColor2 is not Color.Transparent
353    
354    
355     self.lineInfo = self.__GetColorInfo(prop1.GetLineColor(),
356     prop2.GetLineColor(),
357     num)
358    
359     self.fillInfo = self.__GetColorInfo(prop1.GetFill(),
360     prop2.GetFill(),
361     num)
362    
363     self.lineWidth = prop1.GetLineWidth()
364     self.lineWidthStep = (prop2.GetLineWidth() - self.lineWidth) / num
365    
366     return True
367    
368     def next(self):
369     if self.count == 0:
370     raise StopIteration
371    
372     prop = ClassGroupProperties()
373    
374     if self.lineInfo is None:
375     prop.SetLineColor(Color.Transparent)
376     else:
377     prop.SetLineColor(Color(self.lineInfo[CLR][0] / 255,
378     self.lineInfo[CLR][1] / 255,
379     self.lineInfo[CLR][2] / 255))
380    
381     self.lineInfo[CLR][0] += self.lineInfo[STEP][0]
382     self.lineInfo[CLR][1] += self.lineInfo[STEP][1]
383     self.lineInfo[CLR][2] += self.lineInfo[STEP][2]
384    
385     if self.fillInfo is None:
386     prop.SetFill(Color.Transparent)
387     else:
388     prop.SetFill(Color(self.fillInfo[CLR][0] / 255,
389     self.fillInfo[CLR][1] / 255,
390     self.fillInfo[CLR][2] / 255))
391    
392     self.fillInfo[CLR][0] += self.fillInfo[STEP][0]
393     self.fillInfo[CLR][1] += self.fillInfo[STEP][1]
394     self.fillInfo[CLR][2] += self.fillInfo[STEP][2]
395    
396    
397     prop.SetLineWidth(int(self.lineWidth))
398     self.lineWidth += self.lineWidthStep
399    
400     self.count -= 1
401    
402     return prop
403    
404     def __GetColorInfo(self, color1, color2, numGroups):
405    
406     if color1 is Color.Transparent and color2 is Color.Transparent:
407     #
408     # returning early
409     #
410     return None
411     elif color1 is not Color.Transparent and color2 is Color.Transparent:
412     color = [color1.red * 255,
413     color1.green * 255,
414     color1.blue * 255]
415     step = (0, 0, 0)
416     elif color1 is Color.Transparent and color2 is not Color.Transparent:
417     color = [color2.red * 255,
418     color2.green * 255,
419     color2.blue * 255]
420     step = (0, 0, 0)
421     else:
422     color = [color1.red * 255,
423     color1.green * 255,
424     color1.blue * 255]
425     step = ((color2.red * 255 - color1.red * 255) / numGroups,
426     (color2.green * 255 - color1.green * 255) / numGroups,
427     (color2.blue * 255 - color1.blue * 255) / numGroups)
428    
429    
430     return (color, step)
431    
432     class MonochromaticRamp(CustomRamp):
433     def __init__(self, start, end):
434     sp = ClassGroupProperties()
435     sp.SetLineColor(start)
436     sp.SetFill(start)
437    
438     ep = ClassGroupProperties()
439     ep.SetLineColor(end)
440     ep.SetFill(end)
441    
442     CustomRamp.__init__(self, sp, ep)
443    
444     class GreyRamp(MonochromaticRamp):
445     def __init__(self):
446     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(0, 0, 0))
447    
448     class RedRamp(MonochromaticRamp):
449     def __init__(self):
450     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(.8, 0, 0))
451    
452     class GreenRamp(MonochromaticRamp):
453     def __init__(self):
454     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(0, .8, 0))
455    
456     class BlueRamp(MonochromaticRamp):
457     def __init__(self):
458     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(0, 0, .8))
459    
460     class GreenToRedRamp(MonochromaticRamp):
461     def __init__(self):
462     MonochromaticRamp.__init__(self, Color(0, .8, 0), Color(1, 0, 0))
463    
464     class HotToColdRamp:
465    
466     def __iter__(self):
467     return self
468    
469     def GetRamp(self):
470     return self
471    
472     def SetNumGroups(self, num):
473     if num < 0:
474     return False
475    
476     self.num = float(num)
477     self.index = 0
478    
479     return True
480    
481     def next(self):
482     if self.index == self.num:
483     raise StopIteration
484    
485     clr = [1.0, 1.0, 1.0]
486    
487     if self.index < (.25 * self.num):
488     clr[0] = 0
489     clr[1] = 4 * self.index / self.num
490     elif self.index < (.5 * self.num):
491     clr[0] = 0
492     clr[2] = 1 + 4 * (.25 * self.num - self.index) / self.num
493     elif self.index < (.75 * self.num):
494     clr[0] = 4 * (self.index - .5 * self.num) / self.num
495     clr[2] = 0
496     else:
497     clr[1] = 1 + 4 * (.75 * self.num - self.index) / self.num
498     clr[2] = 0
499    
500     self.index += 1
501    
502     prop = ClassGroupProperties()
503     prop.SetLineColor(Color(clr[0], clr[1], clr[2]))
504     prop.SetFill(Color(clr[0], clr[1], clr[2]))
505    
506     return prop
507    
508     #class Colors16Ramp:
509     #
510     #def __iter__(self):
511     #return self
512     #
513     #def GetRamp(self):
514     #return self
515     #
516     #def SetNumGroups(self, num):
517     #if num < 0:
518     #return False
519     #
520     #self.index = 0
521     #
522     #return True

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26