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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 479 - (show annotations)
Thu Mar 6 16:46:07 2003 UTC (22 years ago) by jonathan
Original Path: trunk/thuban/Thuban/Model/classification.py
File MIME type: text/x-python
File size: 20487 byte(s)
Minor documentation changes, Addition of __eq__ and __ne__ methods.
(Classification.SetLayer): prevent recursion between this method
        and Layer.SetClassification().

1 # Copyright (c) 2001, 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 __version__ = "$Revision$"
9
10 """
11 A Classification provides a mapping from an input value
12 to data. This mapping can be specified in two ways.
13 First, specific values can be associated with data.
14 Second, ranges can be associated with data such that if
15 an input value falls with a range that data is returned.
16 If no mapping can be found then default data will
17 be returned. Input values must be hashable objects
18
19 See the description of GetGroup() for more information
20 on the mapping algorithm.
21 """
22
23 # fix for people using python2.1
24 from __future__ import nested_scopes
25
26 from types import *
27
28 from messages import LAYER_PROJECTION_CHANGED, LAYER_LEGEND_CHANGED, \
29 LAYER_VISIBILITY_CHANGED
30
31 from Thuban import _
32 from Thuban.Model.color import Color
33
34 import Thuban.Model.layer
35
36 # constants
37 RANGE_MIN = 0
38 RANGE_MAX = 1
39 RANGE_DATA = 2
40
41 class Classification:
42 """Encapsulates the classification of layer. The Classification
43 divides some kind of data into Groups which are associated with
44 properties. Later the properties can be retrieved by matching
45 data values to the appropriate group."""
46
47 def __init__(self, layer = None, field = None):
48 """Initialize a classification.
49
50 layer -- the Layer object who owns this classification
51
52 field -- the name of the data table field that
53 is to be used to classify layer properties
54 """
55
56 self.layer = None
57 self.field = None
58 self.fieldType = None
59 self.groups = []
60 self.__sendMessages = False
61
62 self.__ToggleMessages(False)
63 self.SetDefaultGroup(ClassGroupDefault())
64 self.SetLayer(layer)
65 self.SetField(field)
66
67 self.__ToggleMessages(True)
68
69 def __iter__(self):
70 return ClassIterator(self.groups)
71
72 def __ToggleMessages(self, on):
73 self.__sendMessages = on
74
75 def __SendMessage(self, message):
76 """Send the message 'message' to the parent layer."""
77 if self.__sendMessages and self.layer is not None:
78 self.layer.changed(message, self.layer)
79
80 def SetField(self, field = None):
81 """Set the name of the data table field to use.
82
83 If there is no layer then the field type is set to None,
84 otherwise the layer is queried to find the type of the
85 field data
86
87 field -- if None then all values map to the default data
88 """
89
90 if field == "":
91 field = None
92
93 self.field = field
94
95 if self.layer is not None:
96 fieldType = self.layer.GetFieldType(field)
97 else:
98 fieldType = None
99
100 self.SetFieldType(fieldType)
101
102 # XXX: if fieldType comes back None then field isn't in the table!
103
104 self.__SendMessage(LAYER_LEGEND_CHANGED)
105
106 def GetField(self):
107 """Return the name of the field."""
108 return self.field
109
110 def GetFieldType(self):
111 """Return the field type."""
112 return self.fieldType
113
114 def SetFieldType(self, type):
115 self.fieldType = type
116
117 def SetLayer(self, layer):
118 """Set the owning Layer of this classification."""
119
120 if __debug__:
121 if layer is not None:
122 assert(isinstance(layer, Thuban.Model.layer.Layer))
123
124 # prevent infinite recursion when calling SetClassification()
125 if self.layer is not None and layer == self.layer:
126 return
127
128 self.layer = layer
129 self.SetField(self.GetField()) # XXX: this sync's the fieldType
130
131 if self.layer is not None:
132 self.layer.SetClassification(self)
133
134 #self.__SendMessage(LAYER_LEGEND_CHANGED)
135
136 def GetLayer(self):
137 """Return the parent layer."""
138 return self.layer
139
140 def SetDefaultGroup(self, group):
141 """Set the group to be used when a value can't be classified.
142
143 group -- group that the value maps to.
144 """
145
146 assert(isinstance(group, ClassGroupDefault))
147 self.AddGroup(group)
148
149 def GetDefaultGroup(self):
150 """Return the default group."""
151 return self.groups[0]
152
153 #
154 # these SetDefault* methods are really only provided for
155 # some backward compatibility. they should be considered
156 # for removal once all the classification code is finished.
157 #
158
159 def SetDefaultFill(self, fill):
160 """Set the default fill color.
161
162 fill -- a Color object.
163 """
164 assert(isinstance(fill, Color))
165 self.GetDefaultGroup().GetProperties().SetFill(fill)
166 self.__SendMessage(LAYER_LEGEND_CHANGED)
167
168 def GetDefaultFill(self):
169 """Return the default fill color."""
170 return self.GetDefaultGroup().GetProperties().GetFill()
171
172 def SetDefaultLineColor(self, color):
173 """Set the default line color.
174
175 color -- a Color object.
176 """
177 assert(isinstance(color, Color))
178 self.GetDefaultGroup().GetProperties().SetLineColor(color)
179 self.__SendMessage(LAYER_LEGEND_CHANGED)
180
181 def GetDefaultLineColor(self):
182 """Return the default line color."""
183 return self.GetDefaultGroup().GetProperties().GetLineColor()
184
185 def SetDefaultLineWidth(self, lineWidth):
186 """Set the default line width.
187
188 lineWidth -- an integer > 0.
189 """
190 assert(isinstance(lineWidth, IntType))
191 self.GetDefaultGroup().GetProperties().SetLineWidth(lineWidth)
192 self.__SendMessage(LAYER_LEGEND_CHANGED)
193
194 def GetDefaultLineWidth(self):
195 """Return the default line width."""
196 return self.GetDefaultGroup().GetProperties().GetLineWidth()
197
198 def AddGroup(self, item):
199 """Add a new ClassGroup item to the classification.
200
201 item -- this must be a valid ClassGroup object
202 """
203
204 assert(isinstance(item, ClassGroup))
205
206 if len(self.groups) > 0 and isinstance(item, ClassGroupDefault):
207 self.groups[0] = item
208 #self.SetDefaultGroup(item)
209 else:
210 self.groups.append(item)
211
212 self.__SendMessage(LAYER_LEGEND_CHANGED)
213
214 def GetGroup(self, value):
215 """Return the associated group, or the default group.
216
217 Groups are checked in the order the were added to the
218 Classification.
219
220 value -- the value to classify. If there is no mapping,
221 the field is None or value is None,
222 return the default properties
223 """
224
225 if self.GetField() is not None and value is not None:
226
227 for i in range(1, len(self.groups)):
228 group = self.groups[i]
229 if group.Matches(value):
230 return group
231
232 return self.GetDefaultGroup()
233
234 def GetProperties(self, value):
235 """Return the properties associated with the given value."""
236
237 group = self.GetGroup(value)
238 if isinstance(group, ClassGroupMap):
239 return group.GetPropertiesFromValue(value)
240 else:
241 return group.GetProperties()
242
243 def TreeInfo(self):
244 items = []
245
246 def build_color_item(text, color):
247 if color is Color.None:
248 return ("%s: %s" % (text, _("None")), None)
249
250 return ("%s: (%.3f, %.3f, %.3f)" %
251 (text, color.red, color.green, color.blue),
252 color)
253
254 def build_item(group, string):
255 label = group.GetLabel()
256 if label == "":
257 label = string
258 else:
259 label += " (%s)" % string
260
261 props = group.GetProperties()
262 i = []
263 v = props.GetLineColor()
264 i.append(build_color_item(_("Line Color"), v))
265 v = props.GetLineWidth()
266 i.append(_("Line Width: %s") % v)
267 v = props.GetFill()
268 i.append(build_color_item(_("Fill"), v))
269 return (label, i)
270
271 for p in self:
272 if isinstance(p, ClassGroupDefault):
273 items.append(build_item(self.GetDefaultGroup(), _("'DEFAULT'")))
274 elif isinstance(p, ClassGroupSingleton):
275 items.append(build_item(p, str(p.GetValue())))
276 elif isinstance(p, ClassGroupRange):
277 items.append(build_item(p, "%s - %s" %
278 (p.GetMin(), p.GetMax())))
279
280 return (_("Classification"), items)
281
282 class ClassIterator:
283 """Allows the Groups in a Classifcation to be interated over.
284
285 The items are returned in the following order:
286 default data, singletons, ranges, maps
287 """
288
289 def __init__(self, data): #default, points, ranges, maps):
290 """Constructor.
291
292 default -- the default group
293
294 points -- a list of singleton groups
295
296 ranges -- a list of range groups
297
298 maps -- a list of map groups
299 """
300
301 self.data = data #[default, points, ranges, maps]
302 self.data_index = 0
303 #self.data_iter = iter(self.data)
304 #self.iter = None
305
306 def __iter__(self):
307 return self
308
309 def next(self):
310 """Return the next item."""
311
312 if self.data_index >= len(self.data):
313 raise StopIteration
314 else:
315 d = self.data[self.data_index]
316 self.data_index += 1
317 return d
318
319 # if self.iter is None:
320 # try:
321 # self.data_item = self.data_iter.next()
322 # self.iter = iter(self.data_item)
323 # except TypeError:
324 # return self.data_item
325
326 # try:
327 # return self.iter.next()
328 # except StopIteration:
329 # self.iter = None
330 # return self.next()
331
332 class ClassGroupProperties:
333 """Represents the properties of a single Classification Group.
334
335 These are used when rendering a layer."""
336
337 def __init__(self, props = None):
338 """Constructor.
339
340 props -- a ClassGroupProperties object. The class is copied if
341 prop is not None. Otherwise, a default set of properties
342 is created such that: line color = Color.Black, line width = 1,
343 and fill color = Color.None
344 """
345
346 self.stroke = None
347 self.strokeWidth = 0
348 self.fill = None
349
350 if props is not None:
351 self.SetProperties(props)
352 else:
353 self.SetLineColor(Color.None)
354 self.SetLineWidth(1)
355 self.SetFill(Color.None)
356
357 def SetProperties(self, props):
358 """Set this class's properties to those in class props."""
359
360 assert(isinstance(props, ClassGroupProperties))
361 self.SetLineColor(props.GetLineColor())
362 self.SetLineWidth(props.GetLineWidth())
363 self.SetFill(props.GetFill())
364
365 def GetLineColor(self):
366 """Return the line color as a Color object."""
367 return self.stroke
368
369 def SetLineColor(self, color):
370 """Set the line color.
371
372 color -- the color of the line. This must be a Color object.
373 """
374
375 assert(isinstance(color, Color))
376 self.stroke = color
377
378 def GetLineWidth(self):
379 """Return the line width."""
380 return self.strokeWidth
381
382 def SetLineWidth(self, lineWidth):
383 """Set the line width.
384
385 lineWidth -- the new line width. This must be > 0.
386 """
387 assert(isinstance(lineWidth, IntType))
388 if (lineWidth < 1):
389 raise ValueError(_("lineWidth < 1"))
390
391 self.strokeWidth = lineWidth
392
393 def GetFill(self):
394 """Return the fill color as a Color object."""
395 return self.fill
396
397 def SetFill(self, fill):
398 """Set the fill color.
399
400 fill -- the color of the fill. This must be a Color object.
401 """
402
403 assert(isinstance(fill, Color))
404 self.fill = fill
405
406 def __eq__(self, other):
407 """Return true if 'props' has the same attributes as this class"""
408
409 return isinstance(other, ClassGroupProperties) \
410 and self.stroke == other.GetLineColor() \
411 and self.strokeWidth == other.GetLineWidth() \
412 and self.fill == other.GetFill()
413
414 def __ne__(self, other):
415 return not self.__eq__(other)
416
417 class ClassGroup:
418 """A base class for all Groups within a Classification"""
419
420 def __init__(self, label = ""):
421 """Constructor.
422
423 label -- A string representing the Group's label
424 """
425
426 self.label = None
427
428 self.SetLabel(label)
429
430 def GetLabel(self):
431 """Return the Group's label."""
432 return self.label
433
434 def SetLabel(self, label):
435 """Set the Group's label.
436
437 label -- a string representing the Group's label. This must
438 not be None.
439 """
440 assert(isinstance(label, StringType))
441 self.label = label
442
443 def Matches(self, value):
444 """Determines if this Group is associated with the given value.
445
446 Returns False. This needs to be overridden by all subclasses.
447 """
448 return False
449
450 def GetProperties(self):
451 """Return the properties associated with the given value.
452
453 Returns None. This needs to be overridden by all subclasses.
454 """
455 return None
456
457
458 class ClassGroupSingleton(ClassGroup):
459 """A Group that is associated with a single value."""
460
461 def __init__(self, value = 0, prop = None, label = ""):
462 """Constructor.
463
464 value -- the associated value.
465
466 prop -- a ClassGroupProperites object. If prop is None a default
467 set of properties is created.
468
469 label -- a label for this group.
470 """
471 ClassGroup.__init__(self, label)
472
473 self.prop = None
474 self.value = None
475
476 self.SetValue(value)
477 self.SetProperties(prop)
478
479 def __copy__(self):
480 return ClassGroupSingleton(self.GetValue(),
481 self.GetProperties(),
482 self.GetLabel())
483
484 def GetValue(self):
485 """Return the associated value."""
486 return self.value
487
488 def SetValue(self, value):
489 """Associate this Group with the given value."""
490 self.value = value
491
492 def Matches(self, value):
493 """Determine if the given value matches the associated Group value."""
494
495 """Returns True if the value matches, False otherwise."""
496
497 return self.value == value
498
499 def GetProperties(self):
500 """Return the Properties associated with this Group."""
501
502 return self.prop
503
504 def SetProperties(self, prop):
505 """Set the properties associated with this Group.
506
507 prop -- a ClassGroupProperties object. if prop is None,
508 a default set of properties is created.
509 """
510
511 if prop is None: prop = ClassGroupProperties()
512 assert(isinstance(prop, ClassGroupProperties))
513 self.prop = prop
514
515 def __eq__(self, other):
516 return isinstance(other, ClassGroupSingleton) \
517 and self.GetProperties() == other.GetProperties() \
518 and self.GetValue() == other.GetValue()
519
520 def __ne__(self, other):
521 return not self.__eq__(other)
522
523 class ClassGroupDefault(ClassGroup):
524 """The default Group. When values do not match any other
525 Group within a Classification, the properties from this
526 class are used."""
527
528 def __init__(self, prop = None, label = ""):
529 """Constructor.
530
531 prop -- a ClassGroupProperites object. If prop is None a default
532 set of properties is created.
533
534 label -- a label for this group.
535 """
536
537 ClassGroup.__init__(self, label)
538 self.SetProperties(prop)
539
540 def __copy__(self):
541 return ClassGroupDefault(self.GetProperties(), self.GetLabel())
542
543 def Matches(self, value):
544 return True
545
546 def GetProperties(self):
547 """Return the Properties associated with this Group."""
548 return self.prop
549
550 def SetProperties(self, prop):
551 """Set the properties associated with this Group.
552
553 prop -- a ClassGroupProperties object. if prop is None,
554 a default set of properties is created.
555 """
556
557 if prop is None: prop = ClassGroupProperties()
558 assert(isinstance(prop, ClassGroupProperties))
559 self.prop = prop
560
561 def __eq__(self, other):
562 return isinstance(other, ClassGroupDefault) \
563 and self.GetProperties() == other.GetProperties()
564
565 def __ne__(self, other):
566 return not self.__eq__(other)
567
568 class ClassGroupRange(ClassGroup):
569 """A Group that represents a range of values that map to the same
570 set of properties."""
571
572 def __init__(self, min = 0, max = 1, prop = None, label = ""):
573 """Constructor.
574
575 The minumum value must be strictly less than the maximum.
576
577 min -- the minimum range value
578
579 max -- the maximum range value
580
581 prop -- a ClassGroupProperites object. If prop is None a default
582 set of properties is created.
583
584 label -- a label for this group.
585 """
586
587 ClassGroup.__init__(self, label)
588
589 self.min = self.max = 0
590 self.prop = None
591
592 self.SetRange(min, max)
593 self.SetProperties(prop)
594
595 def __copy__(self):
596 return ClassGroupRange(self.GetMin(),
597 self.GetMax(),
598 self.GetProperties(),
599 self.GetLabel())
600
601 def GetMin(self):
602 """Return the range's minimum value."""
603 return self.min
604
605 def SetMin(self, min):
606 """Set the range's minimum value.
607
608 min -- the new minimum. Note that this must be less than the current
609 maximum value. Use SetRange() to change both min and max values.
610 """
611
612 self.SetRange(min, self.max)
613
614 def GetMax(self):
615 """Return the range's maximum value."""
616 return self.max
617
618 def SetMax(self, max):
619 """Set the range's maximum value.
620
621 max -- the new maximum. Note that this must be greater than the current
622 minimum value. Use SetRange() to change both min and max values.
623 """
624 self.SetRange(self.min, max)
625
626 def SetRange(self, min, max):
627 """Set a new range.
628
629 Note that min must be strictly less than max.
630
631 min -- the new minimum value
632 min -- the new maximum value
633 """
634
635 if min >= max:
636 raise ValueError(_("ClassGroupRange: %i(min) >= %i(max)!") %
637 (min, max))
638 self.min = min
639 self.max = max
640
641 def GetRange(self):
642 """Return the range as a tuple (min, max)"""
643 return (self.min, self.max)
644
645 def Matches(self, value):
646 """Determine if the given value lies with the current range.
647
648 The following check is used: min <= value < max.
649 """
650
651 return self.min <= value < self.max
652
653 def GetProperties(self):
654 """Return the Properties associated with this Group."""
655 return self.prop
656
657 def SetProperties(self, prop):
658 """Set the properties associated with this Group.
659
660 prop -- a ClassGroupProperties object. if prop is None,
661 a default set of properties is created.
662 """
663 if prop is None: prop = ClassGroupProperties()
664 assert(isinstance(prop, ClassGroupProperties))
665 self.prop = prop
666
667 def __eq__(self, other):
668 return isinstance(other, ClassGroupRange) \
669 and self.GetProperties() == other.GetProperties() \
670 and self.GetRange() == other.GetRange()
671
672 def __ne__(self, other):
673 return not self.__eq__(other)
674
675 class ClassGroupMap(ClassGroup):
676 """Currently, this class is not used."""
677
678 FUNC_ID = "id"
679
680 def __init__(self, map_type = FUNC_ID, func = None, prop = None, label=""):
681 ClassGroup.__init__(self, label)
682
683 self.map_type = map_type
684 self.func = func
685
686 if self.func is None:
687 self.func = func_id
688
689 def Map(self, value):
690 return self.func(value)
691
692 def GetProperties(self):
693 return None
694
695 def GetPropertiesFromValue(self, value):
696 pass
697
698 #
699 # built-in mappings
700 #
701 def func_id(value):
702 return value
703

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26