/[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 886 - (show 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 # 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