/[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 1128 - (show 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 # 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 """
9 Functions to generate Classifications
10 """
11
12 __version__ = "$Revision$"
13 # $Source$
14 # $Id$
15
16 import operator
17
18 from color import Color
19 from range import Range
20 from classification import Classification, ClassGroupSingleton, \
21 ClassGroupRange, ClassGroupProperties
22
23 def GenSingletonsFromList(_list, numGroups, ramp):
24 """Generate a new classification consisting solely of singletons.
25
26 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
30 _list -- any object that implements the iterator interface
31
32 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
37 ramp -- an object which implements the CustomRamp interface
38 """
39
40 clazz = Classification()
41 if numGroups == 0: return clazz
42
43 ramp.SetNumGroups(numGroups)
44
45 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 ramp.SetNumGroups(numGroups)
62
63 for prop in ramp:
64 clazz.AppendGroup(ClassGroupSingleton(cur_value), prop)
65 cur_value += step
66
67 return clazz
68
69 def GenUniformDistribution(min, max, numGroups,
70 ramp, intStep = False):
71 """Generate a classification with numGroups range groups
72 each with the same interval.
73
74 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
80 clazz = Classification()
81 if numGroups == 0: return clazz
82
83 ramp.SetNumGroups(numGroups)
84
85 step = (max - min) / float(numGroups)
86
87 if intStep:
88 step = int(step)
89
90 cur_min = min
91 cur_max = cur_min + step
92
93 i = 0
94 end = "["
95 for prop in ramp:
96
97 if i == (numGroups - 1):
98 cur_max = max
99 end = "]"
100
101
102 # 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
107 cur_min = cur_max
108 cur_max += step
109 i += 1
110
111 return clazz
112
113
114 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
119 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
123 _list -- a sort list of values
124
125 percents -- a sorted list of floats in the range 0.0-1.0 which
126 represent the upper bound of each quantile
127
128 ramp -- an object which implements the CustomRamp interface
129
130 _range -- a Range object
131 """
132
133 clazz = Classification()
134 quantiles = CalculateQuantiles(_list, percents, _range)
135 adjusted = True
136
137 if quantiles is not None:
138
139 numGroups = len(quantiles[3])
140
141 if numGroups != 0:
142
143 adjusted = quantiles[0]
144
145 ramp.SetNumGroups(numGroups)
146
147 start, min, endMax, right = _range.GetRange()
148
149 oldp = 0
150 i = 1
151 end = "]"
152
153 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
160 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
171 return (adjusted, clazz)
172
173
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 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
240 Returns a tuple of the form:
241 (adjusted, minIndex, maxIndex, [quantile_list])
242
243 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
249 Returns None, if no quantiles could be generated based on the
250 given range or input list.
251
252 _list -- a sort list of values
253
254 percents -- a sorted list of floats in the range 0.0-1.0 which
255 represent the upper bound of each quantile
256
257 _range -- a Range object
258 """
259
260 quantiles = []
261 adjusted = False
262
263 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
275 for i in xrange(len(_list)-1, -1, -1):
276 if operator.contains(_range, _list[i]):
277 maxIndex = i
278 break
279
280 numValues = maxIndex - minIndex + 1
281
282 if numValues > 0:
283
284 #
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
293 adjusted = adjusted \
294 or quantiles.has_key(index) \
295 or ((index - minIndex + 1) / float(numValues)) != p
296
297 quantiles[index] = 0
298
299 quantiles = quantiles.keys()
300 quantiles.sort()
301
302 #
303 # the current quantile index must be strictly greater than
304 # the lowerBound
305 #
306 lowerBound = minIndex - 1
307
308 for qindex in xrange(len(quantiles)):
309 if lowerBound >= maxIndex:
310 # discard higher quantiles
311 quantiles = quantiles[:qindex]
312 break
313
314 # lowerBound + 1 is always a valid index
315
316 #
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
326 #
327 # look for similar values around the quantile index
328 #
329 lindex = listIndex - 1
330 while lindex > lowerBound and value == _list[lindex]:
331 lindex -= 1
332 lcount = (listIndex - 1) - lindex
333
334 rindex = listIndex + 1
335 while rindex < maxIndex + 1 and value == _list[rindex]:
336 rindex += 1
337 rcount = (listIndex + 1) - rindex
338
339 #
340 # adjust the current quantile index based on how many
341 # numbers in the _list are the same as the current value
342 #
343 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 # doing so creates an empty quantile.
350 #
351 if lindex != lowerBound:
352 newIndex = lindex
353 else:
354 newIndex = rindex - 1
355
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 newIndex = rindex - 1
364
365 elif rcount < lcount:
366 # there are fewer items to the right, so go to the right
367 newIndex = rindex - 1
368
369 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 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