/[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 886 - (hide annotations)
Fri May 9 16:36:56 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: 16165 byte(s)
New. Contains the classes ClassGenerator, CustomRamp, MonochromaticRamp,
GreyRamp, RedRamp, GreenRamp, BlueRamp, HotToColdRamp from Thuban/UI/classgen.py

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     #print "list:", list
160     #print "percents:", percents
161     #print "numGroups:", numGroups
162    
163     #
164     # find what part of the list range covers
165     #
166     minIndex = -1
167     maxIndex = -2
168     for i in xrange(0, len(list), 1):
169     if operator.contains(_range, list[i]):
170     minIndex = i
171     break
172    
173     for i in xrange(len(list)-1, -1, -1):
174     if operator.contains(_range, list[i]):
175     maxIndex = i
176     break;
177    
178     numValues = maxIndex - minIndex + 1
179     #print "minIndex:", minIndex, "maxIndex:", maxIndex
180     if minIndex <= maxIndex:
181    
182     #
183     # build a list of unique indices into list of where each
184     # quantile should be
185     #
186     quantiles = {}
187     for p in percents:
188     index = min(minIndex + int(p*numValues)-1, maxIndex)
189    
190     adjusted = adjusted \
191     or quantiles.has_key(index) \
192     or ((index+1) / float(numValues)) != p
193    
194     quantiles[index] = 0
195    
196     quantiles = quantiles.keys()
197     quantiles.sort()
198    
199     #print "quantiles:", quantiles
200    
201     #
202     # the current quantile index must be strictly greater than
203     # the lowerBound
204     #
205     lowerBound = minIndex - 1
206    
207     for qindex in range(len(quantiles)):
208     if lowerBound >= maxIndex:
209     # discard higher quantiles
210     quantiles = quantiles[:qindex]
211     break
212    
213     # lowerBound + 1 is always a valid index
214    
215    
216     #
217     # bump up the current quantile index to be a usable index
218     # if it currently falls below the lowerBound
219     #
220     if quantiles[qindex] <= lowerBound:
221     quantiles[qindex] = min(lowerBound + 1, maxIndex)
222    
223     listIndex = quantiles[qindex]
224     value = list[quantiles[qindex]]
225    
226     #print "-----------------------------------"
227     #print "listIndex:", listIndex
228     #print "value:", value
229    
230     #
231     # look for similar values around the quantile index
232     #
233    
234     #print "lowerBound:", lowerBound
235     lindex = listIndex - 1
236     lcount = 0
237     while lindex > lowerBound:
238     if value != list[lindex]: break
239     lcount += 1
240     lindex -= 1
241    
242     rindex = listIndex + 1
243     rcount = 0
244     while rindex < maxIndex + 1:
245     if value != list[rindex]: break
246     rcount += 1
247     rindex += 1
248    
249     #print lcount, "(lcount)", rcount, "(rcount)"
250     #print lindex, "(lindex)", rindex, "(rindex)"
251    
252     #
253     # adjust the current quantile index based on how many
254     # numbers in the list are the same as the current value
255     #
256     newIndex = listIndex
257     if lcount == rcount:
258     if lcount != 0:
259     #
260     # there are an equal number of numbers to the left
261     # and right, try going to the left first unless
262     # doing so creates an empty quantile.
263     #
264     if lindex != lowerBound:
265     newIndex = lindex
266     #quantiles[qindex] = lindex
267     else:
268     newIndex = rindex - 1
269     #quantiles[qindex] = rindex - 1
270    
271     elif lcount < rcount:
272     # there are fewer items to the left, so
273     # try going to the left first unless
274     # doing so creates an empty quantile.
275     if lindex != lowerBound:
276     newIndex = lindex
277     #quantiles[qindex] = lindex
278     else:
279     newIndex = rindex - 1
280     #quantiles[qindex] = rindex - 1
281    
282     elif rcount < lcount:
283     # there are fewer items to the right, so go to the right
284     #quantiles[qindex] = rindex - 1
285     newIndex = rindex - 1
286    
287     quantiles[qindex] = newIndex
288     lowerBound = quantiles[qindex]
289    
290     #print "quantiles:", quantiles
291     #print "lowerBound:", lowerBound
292    
293     #print "================================="
294     #print "quantiles:", quantiles
295     #print "qindex:", qindex
296    
297     return (adjusted, [(q, (q+1) / float(numValues)) for q in quantiles])
298    
299     CLR = 0
300     STEP = 1
301     class CustomRamp:
302    
303     def __init__(self, prop1, prop2):
304     self.prop1 = prop1
305     self.prop2 = prop2
306    
307     self.count = 0
308    
309     def __iter__(self):
310     return self
311    
312     def GetRamp(self):
313     return self
314    
315     def SetNumGroups(self, num):
316    
317     if num <= 0:
318     return False
319    
320     self.count = int(num)
321     num = float(num)
322    
323     prop1 = self.prop1
324     prop2 = self.prop2
325    
326     clr = prop1.GetLineColor()
327     lineColor2 = prop2.GetLineColor()
328    
329     self.noLine = clr is not Color.Transparent \
330     and lineColor2 is not Color.Transparent
331    
332    
333     self.lineInfo = self.__GetColorInfo(prop1.GetLineColor(),
334     prop2.GetLineColor(),
335     num)
336    
337     self.fillInfo = self.__GetColorInfo(prop1.GetFill(),
338     prop2.GetFill(),
339     num)
340    
341     self.lineWidth = prop1.GetLineWidth()
342     self.lineWidthStep = (prop2.GetLineWidth() - self.lineWidth) / num
343    
344     return True
345    
346     def next(self):
347     if self.count == 0:
348     raise StopIteration
349    
350     prop = ClassGroupProperties()
351    
352     if self.lineInfo is None:
353     prop.SetLineColor(Color.Transparent)
354     else:
355     prop.SetLineColor(Color(self.lineInfo[CLR][0] / 255,
356     self.lineInfo[CLR][1] / 255,
357     self.lineInfo[CLR][2] / 255))
358    
359     self.lineInfo[CLR][0] += self.lineInfo[STEP][0]
360     self.lineInfo[CLR][1] += self.lineInfo[STEP][1]
361     self.lineInfo[CLR][2] += self.lineInfo[STEP][2]
362    
363     if self.fillInfo is None:
364     prop.SetFill(Color.Transparent)
365     else:
366     prop.SetFill(Color(self.fillInfo[CLR][0] / 255,
367     self.fillInfo[CLR][1] / 255,
368     self.fillInfo[CLR][2] / 255))
369    
370     self.fillInfo[CLR][0] += self.fillInfo[STEP][0]
371     self.fillInfo[CLR][1] += self.fillInfo[STEP][1]
372     self.fillInfo[CLR][2] += self.fillInfo[STEP][2]
373    
374    
375     prop.SetLineWidth(int(self.lineWidth))
376     self.lineWidth += self.lineWidthStep
377    
378     self.count -= 1
379    
380     return prop
381    
382     def __GetColorInfo(self, color1, color2, numGroups):
383    
384     if color1 is Color.Transparent and color2 is Color.Transparent:
385     #
386     # returning early
387     #
388     return None
389     elif color1 is not Color.Transparent and color2 is Color.Transparent:
390     color = [color1.red * 255,
391     color1.green * 255,
392     color1.blue * 255]
393     step = (0, 0, 0)
394     elif color1 is Color.Transparent and color2 is not Color.Transparent:
395     color = [color2.red * 255,
396     color2.green * 255,
397     color2.blue * 255]
398     step = (0, 0, 0)
399     else:
400     color = [color1.red * 255,
401     color1.green * 255,
402     color1.blue * 255]
403     step = ((color2.red * 255 - color1.red * 255) / numGroups,
404     (color2.green * 255 - color1.green * 255) / numGroups,
405     (color2.blue * 255 - color1.blue * 255) / numGroups)
406    
407    
408     return (color, step)
409    
410     class MonochromaticRamp(CustomRamp):
411     def __init__(self, start, end):
412     sp = ClassGroupProperties()
413     sp.SetLineColor(start)
414     sp.SetFill(start)
415    
416     ep = ClassGroupProperties()
417     ep.SetLineColor(end)
418     ep.SetFill(end)
419    
420     CustomRamp.__init__(self, sp, ep)
421    
422     class GreyRamp(MonochromaticRamp):
423     def __init__(self):
424     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(0, 0, 0))
425    
426     class RedRamp(MonochromaticRamp):
427     def __init__(self):
428     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(.8, 0, 0))
429    
430     class GreenRamp(MonochromaticRamp):
431     def __init__(self):
432     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(0, .8, 0))
433    
434     class BlueRamp(MonochromaticRamp):
435     def __init__(self):
436     MonochromaticRamp.__init__(self, Color(1, 1, 1), Color(0, 0, .8))
437    
438     class GreenToRedRamp(MonochromaticRamp):
439     def __init__(self):
440     MonochromaticRamp.__init__(self, Color(0, .8, 0), Color(1, 0, 0))
441    
442     class HotToColdRamp:
443    
444     def __iter__(self):
445     return self
446    
447     def GetRamp(self):
448     return self
449    
450     def SetNumGroups(self, num):
451     if num < 0:
452     return False
453    
454     self.num = float(num)
455     self.index = 0
456    
457     return True
458    
459     def next(self):
460     if self.index == self.num:
461     raise StopIteration
462    
463     clr = [1.0, 1.0, 1.0]
464    
465     if self.index < (.25 * self.num):
466     clr[0] = 0
467     clr[1] = 4 * self.index / self.num
468     elif self.index < (.5 * self.num):
469     clr[0] = 0
470     clr[2] = 1 + 4 * (.25 * self.num - self.index) / self.num
471     elif self.index < (.75 * self.num):
472     clr[0] = 4 * (self.index - .5 * self.num) / self.num
473     clr[2] = 0
474     else:
475     clr[1] = 1 + 4 * (.75 * self.num - self.index) / self.num
476     clr[2] = 0
477    
478     self.index += 1
479    
480     prop = ClassGroupProperties()
481     prop.SetLineColor(Color(clr[0], clr[1], clr[2]))
482     prop.SetFill(Color(clr[0], clr[1], clr[2]))
483    
484     return prop
485    
486     #class Colors16Ramp:
487     #
488     #def __iter__(self):
489     #return self
490     #
491     #def GetRamp(self):
492     #return self
493     #
494     #def SetNumGroups(self, num):
495     #if num < 0:
496     #return False
497     #
498     #self.index = 0
499     #
500     #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