/[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 462 - (hide annotations)
Wed Mar 5 18:17:17 2003 UTC (22 years ago) by jonathan
Original Path: trunk/thuban/Thuban/Model/classification.py
File MIME type: text/x-python
File size: 18133 byte(s)
Class documentation. Renaming of methods with Stroke to Line.
Groups are stored in a single list with the default as the first element.
Groups are searched in the order they appear in the list.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26