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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 479 - (hide 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 bh 453 # Copyright (c) 2001, 2003 by Intevation GmbH
2 jonathan 371 # 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 jonathan 388 If no mapping can be found then default data will
17 jonathan 371 be returned. Input values must be hashable objects
18    
19 jonathan 449 See the description of GetGroup() for more information
20 jonathan 371 on the mapping algorithm.
21     """
22    
23 jonathan 397 # fix for people using python2.1
24     from __future__ import nested_scopes
25    
26 jonathan 436 from types import *
27    
28 jonathan 388 from messages import LAYER_PROJECTION_CHANGED, LAYER_LEGEND_CHANGED, \
29     LAYER_VISIBILITY_CHANGED
30    
31 jan 374 from Thuban import _
32 jonathan 381 from Thuban.Model.color import Color
33 jan 374
34 jonathan 436 import Thuban.Model.layer
35    
36 jonathan 371 # constants
37     RANGE_MIN = 0
38     RANGE_MAX = 1
39     RANGE_DATA = 2
40    
41     class Classification:
42 jonathan 462 """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 jonathan 371
47 jonathan 410 def __init__(self, layer = None, field = None):
48 jonathan 371 """Initialize a classification.
49    
50 jonathan 462 layer -- the Layer object who owns this classification
51 jonathan 388
52 jonathan 371 field -- the name of the data table field that
53     is to be used to classify layer properties
54     """
55    
56 jonathan 462 self.layer = None
57     self.field = None
58     self.fieldType = None
59     self.groups = []
60     self.__sendMessages = False
61 jonathan 436
62 jonathan 462 self.__ToggleMessages(False)
63 jonathan 436 self.SetDefaultGroup(ClassGroupDefault())
64 jonathan 462 self.SetLayer(layer)
65 jonathan 436 self.SetField(field)
66    
67 jonathan 462 self.__ToggleMessages(True)
68 jonathan 388
69 jonathan 428 def __iter__(self):
70 jonathan 462 return ClassIterator(self.groups)
71 jonathan 428
72 jonathan 462 def __ToggleMessages(self, on):
73     self.__sendMessages = on
74    
75 jonathan 428 def __SendMessage(self, message):
76 jonathan 462 """Send the message 'message' to the parent layer."""
77     if self.__sendMessages and self.layer is not None:
78 jonathan 410 self.layer.changed(message, self.layer)
79    
80 jonathan 462 def SetField(self, field = None):
81 jonathan 371 """Set the name of the data table field to use.
82    
83 jonathan 479 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 jonathan 388 field -- if None then all values map to the default data
88 jonathan 371 """
89    
90 jonathan 436 if field == "":
91     field = None
92    
93 jonathan 371 self.field = field
94 jonathan 462
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 jonathan 428 self.__SendMessage(LAYER_LEGEND_CHANGED)
105 jonathan 371
106 jonathan 388 def GetField(self):
107 jonathan 462 """Return the name of the field."""
108 jonathan 388 return self.field
109    
110 jonathan 462 def GetFieldType(self):
111     """Return the field type."""
112     return self.fieldType
113    
114     def SetFieldType(self, type):
115     self.fieldType = type
116    
117 jonathan 410 def SetLayer(self, layer):
118 jonathan 462 """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 jonathan 479 # prevent infinite recursion when calling SetClassification()
125     if self.layer is not None and layer == self.layer:
126     return
127    
128 jonathan 410 self.layer = layer
129 jonathan 462 self.SetField(self.GetField()) # XXX: this sync's the fieldType
130    
131 jonathan 479 if self.layer is not None:
132     self.layer.SetClassification(self)
133 jonathan 410
134 jonathan 479 #self.__SendMessage(LAYER_LEGEND_CHANGED)
135    
136 jonathan 410 def GetLayer(self):
137 jonathan 462 """Return the parent layer."""
138     return self.layer
139 jonathan 410
140 jonathan 436 def SetDefaultGroup(self, group):
141     """Set the group to be used when a value can't be classified.
142 jonathan 371
143 jonathan 462 group -- group that the value maps to.
144 jonathan 371 """
145    
146 jonathan 436 assert(isinstance(group, ClassGroupDefault))
147 jonathan 462 self.AddGroup(group)
148 jonathan 371
149 jonathan 436 def GetDefaultGroup(self):
150 jonathan 462 """Return the default group."""
151     return self.groups[0]
152 jonathan 385
153 jonathan 428 #
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 jonathan 388 def SetDefaultFill(self, fill):
160 jonathan 462 """Set the default fill color.
161    
162     fill -- a Color object.
163     """
164 jonathan 436 assert(isinstance(fill, Color))
165 jonathan 462 self.GetDefaultGroup().GetProperties().SetFill(fill)
166 jonathan 428 self.__SendMessage(LAYER_LEGEND_CHANGED)
167 jonathan 388
168     def GetDefaultFill(self):
169 jonathan 462 """Return the default fill color."""
170     return self.GetDefaultGroup().GetProperties().GetFill()
171 jonathan 388
172 jonathan 462 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 jonathan 428 self.__SendMessage(LAYER_LEGEND_CHANGED)
180 jonathan 388
181 jonathan 462 def GetDefaultLineColor(self):
182     """Return the default line color."""
183     return self.GetDefaultGroup().GetProperties().GetLineColor()
184 jonathan 388
185 jonathan 462 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 jonathan 428 self.__SendMessage(LAYER_LEGEND_CHANGED)
193 jonathan 388
194 jonathan 462 def GetDefaultLineWidth(self):
195     """Return the default line width."""
196     return self.GetDefaultGroup().GetProperties().GetLineWidth()
197 jonathan 388
198 jonathan 436 def AddGroup(self, item):
199 jonathan 462 """Add a new ClassGroup item to the classification.
200    
201     item -- this must be a valid ClassGroup object
202     """
203    
204 jonathan 436 assert(isinstance(item, ClassGroup))
205 jonathan 371
206 jonathan 462 if len(self.groups) > 0 and isinstance(item, ClassGroupDefault):
207     self.groups[0] = item
208     #self.SetDefaultGroup(item)
209 jonathan 410 else:
210 jonathan 462 self.groups.append(item)
211 jonathan 371
212 jonathan 428 self.__SendMessage(LAYER_LEGEND_CHANGED)
213 jonathan 371
214 jonathan 436 def GetGroup(self, value):
215 jonathan 462 """Return the associated group, or the default group.
216 jonathan 371
217 jonathan 462 Groups are checked in the order the were added to the
218     Classification.
219 jonathan 371
220 jonathan 388 value -- the value to classify. If there is no mapping,
221 jonathan 479 the field is None or value is None,
222     return the default properties
223 jonathan 371 """
224    
225 jonathan 479 if self.GetField() is not None and value is not None:
226 jonathan 371
227 jonathan 462 for i in range(1, len(self.groups)):
228     group = self.groups[i]
229     if group.Matches(value):
230     return group
231 jonathan 371
232 jonathan 462 return self.GetDefaultGroup()
233 jonathan 371
234 jonathan 462 def GetProperties(self, value):
235     """Return the properties associated with the given value."""
236 jonathan 371
237 jonathan 462 group = self.GetGroup(value)
238     if isinstance(group, ClassGroupMap):
239     return group.GetPropertiesFromValue(value)
240     else:
241     return group.GetProperties()
242 jonathan 436
243 jonathan 381 def TreeInfo(self):
244     items = []
245 jonathan 378
246 jonathan 410 def build_color_item(text, color):
247     if color is Color.None:
248     return ("%s: %s" % (text, _("None")), None)
249 jonathan 381
250 jonathan 410 return ("%s: (%.3f, %.3f, %.3f)" %
251     (text, color.red, color.green, color.blue),
252     color)
253 jonathan 381
254 jonathan 436 def build_item(group, string):
255     label = group.GetLabel()
256 jonathan 410 if label == "":
257     label = string
258     else:
259     label += " (%s)" % string
260    
261 jonathan 436 props = group.GetProperties()
262 jonathan 381 i = []
263 jonathan 462 v = props.GetLineColor()
264     i.append(build_color_item(_("Line Color"), v))
265     v = props.GetLineWidth()
266     i.append(_("Line Width: %s") % v)
267 jonathan 436 v = props.GetFill()
268 jonathan 410 i.append(build_color_item(_("Fill"), v))
269     return (label, i)
270 jonathan 388
271 jonathan 428 for p in self:
272 jonathan 436 if isinstance(p, ClassGroupDefault):
273 jonathan 462 items.append(build_item(self.GetDefaultGroup(), _("'DEFAULT'")))
274 jonathan 436 elif isinstance(p, ClassGroupSingleton):
275 jonathan 428 items.append(build_item(p, str(p.GetValue())))
276 jonathan 436 elif isinstance(p, ClassGroupRange):
277 jonathan 428 items.append(build_item(p, "%s - %s" %
278     (p.GetMin(), p.GetMax())))
279 jonathan 388
280 jonathan 436 return (_("Classification"), items)
281 jonathan 381
282 jonathan 428 class ClassIterator:
283 jonathan 462 """Allows the Groups in a Classifcation to be interated over.
284 jonathan 388
285 jonathan 462 The items are returned in the following order:
286     default data, singletons, ranges, maps
287     """
288 jonathan 428
289 jonathan 462 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 jonathan 428 def __iter__(self):
307     return self
308    
309     def next(self):
310 jonathan 462 """Return the next item."""
311 jonathan 428
312 jonathan 462 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 jonathan 428
332 jonathan 436 class ClassGroupProperties:
333 jonathan 462 """Represents the properties of a single Classification Group.
334    
335     These are used when rendering a layer."""
336 jonathan 388
337 jonathan 462 def __init__(self, props = None):
338     """Constructor.
339 jonathan 410
340 jonathan 462 props -- a ClassGroupProperties object. The class is copied if
341     prop is not None. Otherwise, a default set of properties
342 jonathan 479 is created such that: line color = Color.Black, line width = 1,
343     and fill color = Color.None
344 jonathan 462 """
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 jonathan 410 else:
353 jonathan 462 self.SetLineColor(Color.None)
354     self.SetLineWidth(1)
355 jonathan 410 self.SetFill(Color.None)
356    
357 jonathan 462 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 jonathan 388 return self.stroke
368    
369 jonathan 462 def SetLineColor(self, color):
370     """Set the line color.
371 jonathan 388
372 jonathan 462 color -- the color of the line. This must be a Color object.
373     """
374 jonathan 388
375 jonathan 462 assert(isinstance(color, Color))
376     self.stroke = color
377 jonathan 410
378 jonathan 462 def GetLineWidth(self):
379     """Return the line width."""
380     return self.strokeWidth
381 jonathan 388
382 jonathan 462 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 jonathan 388 def GetFill(self):
394 jonathan 462 """Return the fill color as a Color object."""
395 jonathan 388 return self.fill
396    
397     def SetFill(self, fill):
398 jonathan 462 """Set the fill color.
399    
400     fill -- the color of the fill. This must be a Color object.
401     """
402    
403 jonathan 410 assert(isinstance(fill, Color))
404 jonathan 388 self.fill = fill
405    
406 jonathan 479 def __eq__(self, other):
407     """Return true if 'props' has the same attributes as this class"""
408 jonathan 436
409 jonathan 479 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 jonathan 436 class ClassGroup:
418 jonathan 462 """A base class for all Groups within a Classification"""
419 jonathan 436
420     def __init__(self, label = ""):
421 jonathan 462 """Constructor.
422 jonathan 436
423 jonathan 462 label -- A string representing the Group's label
424     """
425    
426     self.label = None
427    
428     self.SetLabel(label)
429    
430 jonathan 388 def GetLabel(self):
431 jonathan 462 """Return the Group's label."""
432 jonathan 388 return self.label
433    
434     def SetLabel(self, label):
435 jonathan 462 """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 jonathan 388 self.label = label
442    
443 jonathan 436 def Matches(self, value):
444 jonathan 462 """Determines if this Group is associated with the given value.
445    
446 jonathan 479 Returns False. This needs to be overridden by all subclasses.
447 jonathan 462 """
448 jonathan 479 return False
449 jonathan 436
450 jonathan 462 def GetProperties(self):
451     """Return the properties associated with the given value.
452    
453 jonathan 479 Returns None. This needs to be overridden by all subclasses.
454 jonathan 462 """
455 jonathan 479 return None
456 jonathan 436
457 jonathan 410
458 jonathan 436 class ClassGroupSingleton(ClassGroup):
459 jonathan 462 """A Group that is associated with a single value."""
460 jonathan 410
461 jonathan 436 def __init__(self, value = 0, prop = None, label = ""):
462 jonathan 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 jonathan 436 ClassGroup.__init__(self, label)
472 jonathan 410
473 jonathan 462 self.prop = None
474     self.value = None
475    
476 jonathan 436 self.SetValue(value)
477     self.SetProperties(prop)
478 jonathan 410
479 jonathan 436 def __copy__(self):
480 jonathan 479 return ClassGroupSingleton(self.GetValue(),
481     self.GetProperties(),
482     self.GetLabel())
483 jonathan 436
484 jonathan 410 def GetValue(self):
485 jonathan 462 """Return the associated value."""
486 jonathan 410 return self.value
487    
488     def SetValue(self, value):
489 jonathan 462 """Associate this Group with the given value."""
490 jonathan 410 self.value = value
491    
492 jonathan 436 def Matches(self, value):
493 jonathan 462 """Determine if the given value matches the associated Group value."""
494    
495     """Returns True if the value matches, False otherwise."""
496    
497 jonathan 436 return self.value == value
498 jonathan 410
499 jonathan 462 def GetProperties(self):
500     """Return the Properties associated with this Group."""
501 jonathan 410
502 jonathan 462 return self.prop
503 jonathan 410
504 jonathan 436 def SetProperties(self, prop):
505 jonathan 462 """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 jonathan 436 if prop is None: prop = ClassGroupProperties()
512     assert(isinstance(prop, ClassGroupProperties))
513     self.prop = prop
514    
515 jonathan 479 def __eq__(self, other):
516     return isinstance(other, ClassGroupSingleton) \
517     and self.GetProperties() == other.GetProperties() \
518     and self.GetValue() == other.GetValue()
519 jonathan 436
520 jonathan 479 def __ne__(self, other):
521     return not self.__eq__(other)
522    
523     class ClassGroupDefault(ClassGroup):
524 jonathan 462 """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 jonathan 436 def __init__(self, prop = None, label = ""):
529 jonathan 462 """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 jonathan 479 ClassGroup.__init__(self, label)
538     self.SetProperties(prop)
539 jonathan 436
540     def __copy__(self):
541 jonathan 479 return ClassGroupDefault(self.GetProperties(), self.GetLabel())
542 jonathan 436
543 jonathan 479 def Matches(self, value):
544     return True
545    
546 jonathan 462 def GetProperties(self):
547     """Return the Properties associated with this Group."""
548 jonathan 436 return self.prop
549    
550 jonathan 479 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 jonathan 436 class ClassGroupRange(ClassGroup):
569 jonathan 462 """A Group that represents a range of values that map to the same
570     set of properties."""
571 jonathan 436
572     def __init__(self, min = 0, max = 1, prop = None, label = ""):
573 jonathan 462 """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 jonathan 436 ClassGroup.__init__(self, label)
588    
589 jonathan 462 self.min = self.max = 0
590     self.prop = None
591    
592 jonathan 410 self.SetRange(min, max)
593 jonathan 436 self.SetProperties(prop)
594 jonathan 410
595 jonathan 436 def __copy__(self):
596 jonathan 479 return ClassGroupRange(self.GetMin(),
597     self.GetMax(),
598     self.GetProperties(),
599     self.GetLabel())
600 jonathan 436
601 jonathan 410 def GetMin(self):
602 jonathan 462 """Return the range's minimum value."""
603 jonathan 410 return self.min
604    
605     def SetMin(self, min):
606 jonathan 462 """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 jonathan 410 self.SetRange(min, self.max)
613    
614     def GetMax(self):
615 jonathan 462 """Return the range's maximum value."""
616 jonathan 410 return self.max
617    
618     def SetMax(self, max):
619 jonathan 462 """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 jonathan 410 self.SetRange(self.min, max)
625    
626     def SetRange(self, min, max):
627 jonathan 462 """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 jonathan 436 if min >= max:
636     raise ValueError(_("ClassGroupRange: %i(min) >= %i(max)!") %
637     (min, max))
638 jonathan 410 self.min = min
639     self.max = max
640    
641     def GetRange(self):
642 jonathan 462 """Return the range as a tuple (min, max)"""
643 jonathan 410 return (self.min, self.max)
644    
645 jonathan 436 def Matches(self, value):
646 jonathan 462 """Determine if the given value lies with the current range.
647    
648     The following check is used: min <= value < max.
649     """
650    
651 jonathan 410 return self.min <= value < self.max
652    
653 jonathan 462 def GetProperties(self):
654     """Return the Properties associated with this Group."""
655     return self.prop
656 jonathan 410
657 jonathan 462 def SetProperties(self, prop):
658     """Set the properties associated with this Group.
659 jonathan 436
660 jonathan 462 prop -- a ClassGroupProperties object. if prop is None,
661     a default set of properties is created.
662     """
663 jonathan 436 if prop is None: prop = ClassGroupProperties()
664     assert(isinstance(prop, ClassGroupProperties))
665     self.prop = prop
666    
667 jonathan 479 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 jonathan 436 class ClassGroupMap(ClassGroup):
676 jonathan 462 """Currently, this class is not used."""
677 jonathan 436
678 jonathan 410 FUNC_ID = "id"
679    
680 jonathan 436 def __init__(self, map_type = FUNC_ID, func = None, prop = None, label=""):
681 jonathan 462 ClassGroup.__init__(self, label)
682 jonathan 410
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 jonathan 462 def GetProperties(self):
693     return None
694    
695     def GetPropertiesFromValue(self, value):
696     pass
697    
698 jonathan 410 #
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