/[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 959 - (hide annotations)
Wed May 21 17:22:58 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: 16791 byte(s)
(ClassGenerator.GenQuantiles): Remove
        references to 'inf' and use new Range __init__ to pass floats
        directly rather than converting them to strings first.
        Fixes RTBug #1876.

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 jonathan 895 def GenSingletonsFromList(self, _list, numGroups, ramp):
18 jonathan 886 """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 jonathan 895 could be fewer groups if '_list' contains fewer that 'numGroups' items.
23 jonathan 886
24 jonathan 895 _list -- any object that implements the iterator interface
25 jonathan 886
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 jonathan 900 ramp -- an object which implements the CustomRamp interface
32 jonathan 886 """
33    
34     clazz = Classification()
35     if numGroups == 0: return clazz
36    
37     ramp.SetNumGroups(numGroups)
38    
39 jonathan 895 for value, prop in zip(_list, ramp):
40 jonathan 886 clazz.AppendGroup(ClassGroupSingleton(value, prop))
41    
42     return clazz
43    
44     def GenSingletons(self, min, max, numGroups, ramp):
45    
46     clazz = Classification()
47    
48     #step = int((max - min) / float(numGroups))
49    
50     if numGroups > 0:
51    
52     step = int((max - min + 1) / float(numGroups))
53     cur_value = min
54    
55     ramp.SetNumGroups(numGroups)
56    
57     for prop in ramp:
58     clazz.AppendGroup(ClassGroupSingleton(cur_value), prop)
59     cur_value += step
60    
61     return clazz
62    
63 jonathan 900 def GenUniformDistribution(self, min, max, numGroups,
64 jonathan 886 ramp, intStep = False):
65     """Generate a classification with numGroups range groups
66     each with the same interval.
67    
68     intStep -- force the calculated stepping to an integer.
69     Useful if the values are integers but the
70     number of groups specified doesn't evenly
71     divide (max - min).
72     """
73    
74     clazz = Classification()
75     if numGroups == 0: return clazz
76    
77     ramp.SetNumGroups(numGroups)
78    
79     step = (max - min) / float(numGroups)
80    
81     if intStep:
82     step = int(step)
83    
84     cur_min = min
85     cur_max = cur_min + step
86    
87     i = 0
88     end = "["
89     for prop in ramp:
90    
91     if i == (numGroups - 1):
92     cur_max = max
93     end = "]"
94    
95    
96     # this check guards against rounding issues
97     if cur_min != cur_max:
98     range = Range("[" + str(float(cur_min)) + ";" +
99     str(float(cur_max)) + end)
100     clazz.AppendGroup(ClassGroupRange(range, None, prop))
101    
102     cur_min = cur_max
103     cur_max += step
104     i += 1
105    
106     return clazz
107    
108    
109 jonathan 895 def GenQuantiles(self, _list, percents, ramp, _range):
110     """Generates a Classification which has groups of ranges that
111     represent quantiles of _list at the percentages given in percents.
112     Only the values that fall within _range are considered.
113    
114     Returns a tuple (adjusted, Classification) where adjusted is
115     True if the Classification does not exactly represent the given
116     range, or if the Classification is empty.
117    
118     _list -- a sort list of values
119    
120     percents -- a sorted list of floats in the range 0.0-1.0 which
121     represent the upper bound of each quantile
122    
123     ramp -- an object which implements the CustomRamp interface
124    
125     _range -- a Range object
126     """
127    
128 jonathan 886 clazz = Classification()
129 jonathan 895 quantiles = self.CalculateQuantiles(_list, percents, _range)
130     adjusted = True
131 jonathan 886
132 jonathan 895 if quantiles is not None:
133 jonathan 886
134 jonathan 895 numGroups = len(quantiles[3])
135 jonathan 886
136 jonathan 895 if numGroups != 0:
137 jonathan 886
138 jonathan 895 adjusted = quantiles[0]
139 jonathan 886
140 jonathan 895 ramp.SetNumGroups(numGroups)
141 jonathan 886
142 jonathan 900 start, min, endMax, right = _range.GetRange()
143    
144 jonathan 895 oldp = 0
145 jonathan 900 i = 1
146     end = "]"
147    
148 jonathan 895 for (q, p), prop in zip(quantiles[3], ramp):
149 jonathan 900 if i == numGroups:
150     max = endMax
151     end = right
152     else:
153     max = _list[q]
154    
155 jonathan 959 group = ClassGroupRange(Range((start, min, max, end)),
156 jonathan 895 None, prop)
157    
158     group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),
159     round(p*100, 2)))
160     oldp = p
161     start = "]"
162     min = max
163     clazz.AppendGroup(group)
164 jonathan 900 i += 1
165 jonathan 895
166     return (adjusted, clazz)
167    
168     def CalculateQuantiles(self, _list, percents, _range):
169     """Calculate quantiles for the given _list of percents from the
170 jonathan 886 sorted list of values that are in range.
171    
172 jonathan 895 This may not actually generate len(percents) quantiles if
173 jonathan 886 many of the values that fall on quantile borders are the same.
174    
175 jonathan 895 Returns a tuple of the form:
176     (adjusted, minIndex, maxIndex, [quantile_list])
177 jonathan 886
178 jonathan 895 where adjusted is True if the the quantile percentages differ from
179     those supplied, minIndex is the index into _list where the
180     minimum value used is located, maxIndex is the index into _list
181     where the maximum value used is located, and quantile_list is a
182     list of tuples of the form: (list_index, quantile_percentage)
183    
184     Returns None, if no quantiles could be generated based on the
185     given range or input list.
186    
187     _list -- a sort list of values
188    
189     percents -- a sorted list of floats in the range 0.0-1.0 which
190     represent the upper bound of each quantile
191    
192     _range -- a Range object
193 jonathan 886 """
194    
195     quantiles = []
196     adjusted = False
197 jonathan 895
198 jonathan 886 if len(percents) != 0:
199    
200     #
201 jonathan 895 # find what part of the _list range covers
202 jonathan 886 #
203     minIndex = -1
204     maxIndex = -2
205 jonathan 895 for i in xrange(0, len(_list), 1):
206     if operator.contains(_range, _list[i]):
207 jonathan 886 minIndex = i
208     break
209    
210 jonathan 895 for i in xrange(len(_list)-1, -1, -1):
211     if operator.contains(_range, _list[i]):
212 jonathan 886 maxIndex = i
213 jonathan 895 break
214 jonathan 886
215     numValues = maxIndex - minIndex + 1
216    
217 jonathan 895 if numValues > 0:
218    
219 jonathan 886 #
220     # build a list of unique indices into list of where each
221 jonathan 891 # quantile *should* be. set adjusted if the resulting
222     # indices are different
223 jonathan 886 #
224     quantiles = {}
225     for p in percents:
226     index = min(minIndex + int(p*numValues)-1, maxIndex)
227    
228     adjusted = adjusted \
229     or quantiles.has_key(index) \
230 jonathan 891 or ((index - minIndex + 1) / float(numValues)) != p
231 jonathan 886
232     quantiles[index] = 0
233    
234     quantiles = quantiles.keys()
235     quantiles.sort()
236    
237     #
238     # the current quantile index must be strictly greater than
239     # the lowerBound
240     #
241     lowerBound = minIndex - 1
242    
243 jonathan 895 for qindex in xrange(len(quantiles)):
244 jonathan 886 if lowerBound >= maxIndex:
245     # discard higher quantiles
246     quantiles = quantiles[:qindex]
247     break
248    
249     # lowerBound + 1 is always a valid index
250    
251     #
252     # bump up the current quantile index to be a usable index
253     # if it currently falls below the lowerBound
254     #
255     if quantiles[qindex] <= lowerBound:
256 jonathan 895 quantiles[qindex] = lowerBound + 1
257 jonathan 886
258     listIndex = quantiles[qindex]
259 jonathan 895 value = _list[listIndex]
260 jonathan 886
261     #
262     # look for similar values around the quantile index
263     #
264     lindex = listIndex - 1
265 jonathan 895 while lindex > lowerBound and value == _list[lindex]:
266 jonathan 886 lindex -= 1
267 jonathan 895 lcount = (listIndex - 1) - lindex
268 jonathan 886
269     rindex = listIndex + 1
270 jonathan 895 while rindex < maxIndex + 1 and value == _list[rindex]:
271 jonathan 886 rindex += 1
272 jonathan 895 rcount = (listIndex + 1) - rindex
273 jonathan 886
274     #
275     # adjust the current quantile index based on how many
276 jonathan 895 # numbers in the _list are the same as the current value
277 jonathan 886 #
278     newIndex = listIndex
279     if lcount == rcount:
280     if lcount != 0:
281     #
282     # there are an equal number of numbers to the left
283     # and right, try going to the left first unless
284     # doing so creates an empty quantile.
285     #
286     if lindex != lowerBound:
287     newIndex = lindex
288     else:
289     newIndex = rindex - 1
290    
291     elif lcount < rcount:
292     # there are fewer items to the left, so
293     # try going to the left first unless
294     # doing so creates an empty quantile.
295     if lindex != lowerBound:
296     newIndex = lindex
297     else:
298     newIndex = rindex - 1
299    
300     elif rcount < lcount:
301     # there are fewer items to the right, so go to the right
302     newIndex = rindex - 1
303    
304 jonathan 895 adjusted = adjusted or newIndex != listIndex
305    
306 jonathan 886 quantiles[qindex] = newIndex
307     lowerBound = quantiles[qindex]
308    
309 jonathan 891 #
310     # since quantiles is only set if the code is at least a little
311     # successful, an empty list will be generated in the case that
312     # we fail to get to the real body of the algorithm
313     #
314 jonathan 895 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 jonathan 886
321     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