/[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 891 - (hide annotations)
Fri May 9 18:08:46 2003 UTC (21 years, 10 months ago) by jonathan
Original Path: trunk/thuban/Thuban/Model/classgen.py
File MIME type: text/x-python
File size: 15325 byte(s)
(ClassGenerator.CalculateQuantiles):
        Clean up debugging statement, add comments, fix a small bug in the
        returned adjusted percentiles.

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