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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 891 - (show 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 # 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 # quantile *should* be. set adjusted if the resulting
180 # indices are different
181 #
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 or ((index - minIndex + 1) / float(numValues)) != p
189
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 #
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
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