/[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 1098 - (show annotations)
Fri May 30 06:27:44 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: 15606 byte(s)
Remove ClassGenerator class but make
        all the methods functions. Fixes RTBug #1903.

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 def CalculateQuantiles(_list, percents, _range):
174 """Calculate quantiles for the given _list of percents from the
175 sorted list of values that are in range.
176
177 This may not actually generate len(percents) quantiles if
178 many of the values that fall on quantile borders are the same.
179
180 Returns a tuple of the form:
181 (adjusted, minIndex, maxIndex, [quantile_list])
182
183 where adjusted is True if the the quantile percentages differ from
184 those supplied, minIndex is the index into _list where the
185 minimum value used is located, maxIndex is the index into _list
186 where the maximum value used is located, and quantile_list is a
187 list of tuples of the form: (list_index, quantile_percentage)
188
189 Returns None, if no quantiles could be generated based on the
190 given range or input list.
191
192 _list -- a sort list of values
193
194 percents -- a sorted list of floats in the range 0.0-1.0 which
195 represent the upper bound of each quantile
196
197 _range -- a Range object
198 """
199
200 quantiles = []
201 adjusted = False
202
203 if len(percents) != 0:
204
205 #
206 # find what part of the _list range covers
207 #
208 minIndex = -1
209 maxIndex = -2
210 for i in xrange(0, len(_list), 1):
211 if operator.contains(_range, _list[i]):
212 minIndex = i
213 break
214
215 for i in xrange(len(_list)-1, -1, -1):
216 if operator.contains(_range, _list[i]):
217 maxIndex = i
218 break
219
220 numValues = maxIndex - minIndex + 1
221
222 if numValues > 0:
223
224 #
225 # build a list of unique indices into list of where each
226 # quantile *should* be. set adjusted if the resulting
227 # indices are different
228 #
229 quantiles = {}
230 for p in percents:
231 index = min(minIndex + int(p*numValues)-1, maxIndex)
232
233 adjusted = adjusted \
234 or quantiles.has_key(index) \
235 or ((index - minIndex + 1) / float(numValues)) != p
236
237 quantiles[index] = 0
238
239 quantiles = quantiles.keys()
240 quantiles.sort()
241
242 #
243 # the current quantile index must be strictly greater than
244 # the lowerBound
245 #
246 lowerBound = minIndex - 1
247
248 for qindex in xrange(len(quantiles)):
249 if lowerBound >= maxIndex:
250 # discard higher quantiles
251 quantiles = quantiles[:qindex]
252 break
253
254 # lowerBound + 1 is always a valid index
255
256 #
257 # bump up the current quantile index to be a usable index
258 # if it currently falls below the lowerBound
259 #
260 if quantiles[qindex] <= lowerBound:
261 quantiles[qindex] = lowerBound + 1
262
263 listIndex = quantiles[qindex]
264 value = _list[listIndex]
265
266 #
267 # look for similar values around the quantile index
268 #
269 lindex = listIndex - 1
270 while lindex > lowerBound and value == _list[lindex]:
271 lindex -= 1
272 lcount = (listIndex - 1) - lindex
273
274 rindex = listIndex + 1
275 while rindex < maxIndex + 1 and value == _list[rindex]:
276 rindex += 1
277 rcount = (listIndex + 1) - rindex
278
279 #
280 # adjust the current quantile index based on how many
281 # numbers in the _list are the same as the current value
282 #
283 newIndex = listIndex
284 if lcount == rcount:
285 if lcount != 0:
286 #
287 # there are an equal number of numbers to the left
288 # and right, try going to the left first unless
289 # doing so creates an empty quantile.
290 #
291 if lindex != lowerBound:
292 newIndex = lindex
293 else:
294 newIndex = rindex - 1
295
296 elif lcount < rcount:
297 # there are fewer items to the left, so
298 # try going to the left first unless
299 # doing so creates an empty quantile.
300 if lindex != lowerBound:
301 newIndex = lindex
302 else:
303 newIndex = rindex - 1
304
305 elif rcount < lcount:
306 # there are fewer items to the right, so go to the right
307 newIndex = rindex - 1
308
309 adjusted = adjusted or newIndex != listIndex
310
311 quantiles[qindex] = newIndex
312 lowerBound = quantiles[qindex]
313
314 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
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