/[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 1128 - (hide annotations)
Tue Jun 3 17:00:47 2003 UTC (21 years, 9 months ago) by tkoester
Original Path: trunk/thuban/Thuban/Model/classgen.py
File MIME type: text/x-python
File size: 17311 byte(s)
* Thuban/Model/classgen.py (GenQuantiles0): New function.

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 tkoester 1128
174     def GenQuantiles0(_list, percents, ramp, _range):
175     """Same as GenQuantiles, but the first class won't be added to
176     the classification.
177    
178     Returns a tuple (adjusted, Classification, upper_class0) where
179     upper_class0 is the highest value inside the first class.
180    
181     _list -- a sort list of values
182    
183     percents -- a sorted list of floats in the range 0.0-1.0 which
184     represent the upper bound of each quantile
185    
186     ramp -- an object which implements the CustomRamp interface
187    
188     _range -- a Range object
189     """
190    
191     clazz = Classification()
192     quantiles = CalculateQuantiles(_list, percents, _range)
193     adjusted = True
194    
195     if quantiles is not None:
196    
197     numGroups = len(quantiles[3]) - 1
198    
199     if numGroups > 0:
200     adjusted = quantiles[0]
201    
202     ramp.SetNumGroups(numGroups)
203    
204     start, min, endMax, right = _range.GetRange()
205    
206     class0 = quantiles[3][0]
207     min = _list[class0[0]]
208     oldp = class0[1]
209     i = 1
210     end = "]"
211    
212     for (q, p), prop in zip(quantiles[3][1:], ramp):
213     if i == numGroups:
214     max = endMax
215     end = right
216     else:
217     max = _list[q]
218    
219     group = ClassGroupRange(Range((start, min, max, end)),
220     None, prop)
221    
222     group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),
223     round(p*100, 2)))
224     oldp = p
225     start = "]"
226     min = max
227     clazz.AppendGroup(group)
228     i += 1
229    
230     return (adjusted, clazz, _list[class0[0]])
231    
232    
233 jonathan 1098 def CalculateQuantiles(_list, percents, _range):
234     """Calculate quantiles for the given _list of percents from the
235     sorted list of values that are in range.
236    
237     This may not actually generate len(percents) quantiles if
238     many of the values that fall on quantile borders are the same.
239 jonathan 886
240 jonathan 1098 Returns a tuple of the form:
241     (adjusted, minIndex, maxIndex, [quantile_list])
242 jonathan 886
243 jonathan 1098 where adjusted is True if the the quantile percentages differ from
244     those supplied, minIndex is the index into _list where the
245     minimum value used is located, maxIndex is the index into _list
246     where the maximum value used is located, and quantile_list is a
247     list of tuples of the form: (list_index, quantile_percentage)
248 jonathan 886
249 jonathan 1098 Returns None, if no quantiles could be generated based on the
250     given range or input list.
251 jonathan 900
252 jonathan 1098 _list -- a sort list of values
253 jonathan 900
254 jonathan 1098 percents -- a sorted list of floats in the range 0.0-1.0 which
255     represent the upper bound of each quantile
256 jonathan 900
257 jonathan 1098 _range -- a Range object
258     """
259 jonathan 895
260 jonathan 1098 quantiles = []
261     adjusted = False
262 jonathan 895
263 jonathan 1098 if len(percents) != 0:
264    
265     #
266     # find what part of the _list range covers
267     #
268     minIndex = -1
269     maxIndex = -2
270     for i in xrange(0, len(_list), 1):
271     if operator.contains(_range, _list[i]):
272     minIndex = i
273     break
274 jonathan 886
275 jonathan 1098 for i in xrange(len(_list)-1, -1, -1):
276     if operator.contains(_range, _list[i]):
277     maxIndex = i
278     break
279 jonathan 886
280 jonathan 1098 numValues = maxIndex - minIndex + 1
281 jonathan 895
282 jonathan 1098 if numValues > 0:
283 jonathan 895
284 jonathan 1098 #
285     # build a list of unique indices into list of where each
286     # quantile *should* be. set adjusted if the resulting
287     # indices are different
288     #
289     quantiles = {}
290     for p in percents:
291     index = min(minIndex + int(p*numValues)-1, maxIndex)
292 jonathan 895
293 jonathan 1098 adjusted = adjusted \
294     or quantiles.has_key(index) \
295     or ((index - minIndex + 1) / float(numValues)) != p
296 jonathan 895
297 jonathan 1098 quantiles[index] = 0
298 jonathan 895
299 jonathan 1098 quantiles = quantiles.keys()
300     quantiles.sort()
301    
302 jonathan 886 #
303 jonathan 1098 # the current quantile index must be strictly greater than
304     # the lowerBound
305 jonathan 886 #
306 jonathan 1098 lowerBound = minIndex - 1
307 jonathan 886
308 jonathan 1098 for qindex in xrange(len(quantiles)):
309     if lowerBound >= maxIndex:
310     # discard higher quantiles
311     quantiles = quantiles[:qindex]
312 jonathan 895 break
313 jonathan 886
314 jonathan 1098 # lowerBound + 1 is always a valid index
315 jonathan 886
316 jonathan 1098 #
317     # bump up the current quantile index to be a usable index
318     # if it currently falls below the lowerBound
319     #
320     if quantiles[qindex] <= lowerBound:
321     quantiles[qindex] = lowerBound + 1
322    
323     listIndex = quantiles[qindex]
324     value = _list[listIndex]
325 jonathan 895
326 jonathan 886 #
327 jonathan 1098 # look for similar values around the quantile index
328 jonathan 886 #
329 jonathan 1098 lindex = listIndex - 1
330     while lindex > lowerBound and value == _list[lindex]:
331     lindex -= 1
332     lcount = (listIndex - 1) - lindex
333 jonathan 886
334 jonathan 1098 rindex = listIndex + 1
335     while rindex < maxIndex + 1 and value == _list[rindex]:
336     rindex += 1
337     rcount = (listIndex + 1) - rindex
338 jonathan 886
339     #
340 jonathan 1098 # adjust the current quantile index based on how many
341     # numbers in the _list are the same as the current value
342 jonathan 886 #
343 jonathan 1098 newIndex = listIndex
344     if lcount == rcount:
345     if lcount != 0:
346     #
347     # there are an equal number of numbers to the left
348     # and right, try going to the left first unless
349 jonathan 886 # doing so creates an empty quantile.
350 jonathan 1098 #
351 jonathan 886 if lindex != lowerBound:
352     newIndex = lindex
353     else:
354     newIndex = rindex - 1
355 jonathan 1098
356     elif lcount < rcount:
357     # there are fewer items to the left, so
358     # try going to the left first unless
359     # doing so creates an empty quantile.
360     if lindex != lowerBound:
361     newIndex = lindex
362     else:
363 jonathan 886 newIndex = rindex - 1
364 jonathan 895
365 jonathan 1098 elif rcount < lcount:
366     # there are fewer items to the right, so go to the right
367     newIndex = rindex - 1
368 jonathan 886
369 jonathan 1098 adjusted = adjusted or newIndex != listIndex
370    
371     quantiles[qindex] = newIndex
372     lowerBound = quantiles[qindex]
373    
374     if len(quantiles) == 0:
375     return None
376     else:
377     return (adjusted, minIndex, maxIndex,
378     [(q, (q - minIndex+1) / float(numValues)) \
379     for q in quantiles])
380    
381 jonathan 886 CLR = 0
382     STEP = 1
383     class CustomRamp:
384    
385     def __init__(self, prop1, prop2):
386     self.prop1 = prop1
387     self.prop2 = prop2
388    
389     self.count = 0
390    
391     def __iter__(self):
392     return self
393    
394     def GetRamp(self):
395     return self
396    
397     def SetNumGroups(self, num):
398    
399     if num <= 0:
400     return False
401    
402     self.count = int(num)
403     num = float(num)
404    
405     prop1 = self.prop1
406     prop2 = self.prop2
407    
408     clr = prop1.GetLineColor()
409     lineColor2 = prop2.GetLineColor()
410    
411     self.noLine = clr is not Color.Transparent \
412     and lineColor2 is not Color.Transparent
413    
414    
415     self.lineInfo = self.__GetColorInfo(prop1.GetLineColor(),
416     prop2.GetLineColor(),
417     num)
418    
419     self.fillInfo = self.__GetColorInfo(prop1.GetFill(),
420     prop2.GetFill(),
421     num)
422    
423     self.lineWidth = prop1.GetLineWidth()
424     self.lineWidthStep = (prop2.GetLineWidth() - self.lineWidth) / num
425    
426     return True
427    
428     def next(self):
429     if self.count == 0:
430     raise StopIteration
431    
432     prop = ClassGroupProperties()
433    
434     if self.lineInfo is None:
435     prop.SetLineColor(Color.Transparent)
436     else:
437     prop.SetLineColor(Color(self.lineInfo[CLR][0] / 255,
438     self.lineInfo[CLR][1] / 255,
439     self.lineInfo[CLR][2] / 255))
440    
441     self.lineInfo[CLR][0] += self.lineInfo[STEP][0]
442     self.lineInfo[CLR][1] += self.lineInfo[STEP][1]
443     self.lineInfo[CLR][2] += self.lineInfo[STEP][2]
444    
445     if self.fillInfo is None:
446     prop.SetFill(Color.Transparent)
447     else:
448     prop.SetFill(Color(self.fillInfo[CLR][0] / 255,
449     self.fillInfo[CLR][1] / 255,
450     self.fillInfo[CLR][2] / 255))
451    
452     self.fillInfo[CLR][0] += self.fillInfo[STEP][0]
453     self.fillInfo[CLR][1] += self.fillInfo[STEP][1]
454     self.fillInfo[CLR][2] += self.fillInfo[STEP][2]
455    
456    
457     prop.SetLineWidth(int(self.lineWidth))
458     self.lineWidth += self.lineWidthStep
459    
460     self.count -= 1
461    
462     return prop
463    
464     def __GetColorInfo(self, color1, color2, numGroups):
465    
466     if color1 is Color.Transparent and color2 is Color.Transparent:
467     #
468     # returning early
469     #
470     return None
471     elif color1 is not Color.Transparent and color2 is Color.Transparent:
472     color = [color1.red * 255,
473     color1.green * 255,
474     color1.blue * 255]
475     step = (0, 0, 0)
476     elif color1 is Color.Transparent and color2 is not Color.Transparent:
477     color = [color2.red * 255,
478     color2.green * 255,
479     color2.blue * 255]
480     step = (0, 0, 0)
481     else:
482     color = [color1.red * 255,
483     color1.green * 255,
484     color1.blue * 255]
485     step = ((color2.red * 255 - color1.red * 255) / numGroups,
486     (color2.green * 255 - color1.green * 255) / numGroups,
487     (color2.blue * 255 - color1.blue * 255) / numGroups)
488    
489    
490     return (color, step)
491    
492     class MonochromaticRamp(CustomRamp):
493     def __init__(self, start, end):
494     sp = ClassGroupProperties()
495     sp.SetLineColor(start)
496     sp.SetFill(start)
497    
498     ep = ClassGroupProperties()
499     ep.SetLineColor(end)
500     ep.SetFill(end)
501    
502     CustomRamp.__init__(self, sp, ep)
503    
504     class GreyRamp(MonochromaticRamp):
505     def __init__(self):
506     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(0, 0, 0))
507    
508     class RedRamp(MonochromaticRamp):
509     def __init__(self):
510     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(.8, 0, 0))
511    
512     class GreenRamp(MonochromaticRamp):
513     def __init__(self):
514     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(0, .8, 0))
515    
516     class BlueRamp(MonochromaticRamp):
517     def __init__(self):
518     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(0, 0, .8))
519    
520     class GreenToRedRamp(MonochromaticRamp):
521     def __init__(self):
522     MonochromaticRamp.__init__(self, Color(0, .8, 0), Color(1, 0, 0))
523    
524     class HotToColdRamp:
525    
526     def __iter__(self):
527     return self
528    
529     def GetRamp(self):
530     return self
531    
532     def SetNumGroups(self, num):
533     if num < 0:
534     return False
535    
536     self.num = float(num)
537     self.index = 0
538    
539     return True
540    
541     def next(self):
542     if self.index == self.num:
543     raise StopIteration
544    
545     clr = [1.0, 1.0, 1.0]
546    
547     if self.index < (.25 * self.num):
548     clr[0] = 0
549     clr[1] = 4 * self.index / self.num
550     elif self.index < (.5 * self.num):
551     clr[0] = 0
552     clr[2] = 1 + 4 * (.25 * self.num - self.index) / self.num
553     elif self.index < (.75 * self.num):
554     clr[0] = 4 * (self.index - .5 * self.num) / self.num
555     clr[2] = 0
556     else:
557     clr[1] = 1 + 4 * (.75 * self.num - self.index) / self.num
558     clr[2] = 0
559    
560     self.index += 1
561    
562     prop = ClassGroupProperties()
563     prop.SetLineColor(Color(clr[0], clr[1], clr[2]))
564     prop.SetFill(Color(clr[0], clr[1], clr[2]))
565    
566     return prop
567    
568     #class Colors16Ramp:
569     #
570     #def __iter__(self):
571     #return self
572     #
573     #def GetRamp(self):
574     #return self
575     #
576     #def SetNumGroups(self, num):
577     #if num < 0:
578     #return False
579     #
580     #self.index = 0
581     #
582     #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