/[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 462 - (show 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 # 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
37 # constants
38 RANGE_MIN = 0
39 RANGE_MAX = 1
40 RANGE_DATA = 2
41
42 class Classification:
43 """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
48 def __init__(self, layer = None, field = None):
49 """Initialize a classification.
50
51 layer -- the Layer object who owns this classification
52
53 field -- the name of the data table field that
54 is to be used to classify layer properties
55 """
56
57 self.layer = None
58 self.field = None
59 self.fieldType = None
60 self.groups = []
61 self.__sendMessages = False
62
63 self.__ToggleMessages(False)
64 self.SetDefaultGroup(ClassGroupDefault())
65 self.SetLayer(layer)
66 self.SetField(field)
67
68 self.__ToggleMessages(True)
69
70 def __iter__(self):
71 return ClassIterator(self.groups)
72
73 def __ToggleMessages(self, on):
74 self.__sendMessages = on
75
76 def __SendMessage(self, message):
77 """Send the message 'message' to the parent layer."""
78 if self.__sendMessages and self.layer is not None:
79 self.layer.changed(message, self.layer)
80
81 def SetField(self, field = None):
82 """Set the name of the data table field to use.
83
84 field -- if None then all values map to the default data
85 """
86
87 if field == "":
88 field = None
89
90 self.field = field
91
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 self.__SendMessage(LAYER_LEGEND_CHANGED)
102
103 def GetField(self):
104 """Return the name of the field."""
105 return self.field
106
107 def GetFieldType(self):
108 """Return the field type."""
109 return self.fieldType
110
111 def SetFieldType(self, type):
112 self.fieldType = type
113
114 def SetLayer(self, layer):
115 """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 self.layer = layer
122 self.SetField(self.GetField()) # XXX: this sync's the fieldType
123
124 self.__SendMessage(LAYER_LEGEND_CHANGED)
125
126 def GetLayer(self):
127 """Return the parent layer."""
128 return self.layer
129
130 def SetDefaultGroup(self, group):
131 """Set the group to be used when a value can't be classified.
132
133 group -- group that the value maps to.
134 """
135
136 assert(isinstance(group, ClassGroupDefault))
137 self.AddGroup(group)
138
139 def GetDefaultGroup(self):
140 """Return the default group."""
141 return self.groups[0]
142
143 #
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 def SetDefaultFill(self, fill):
150 """Set the default fill color.
151
152 fill -- a Color object.
153 """
154 assert(isinstance(fill, Color))
155 self.GetDefaultGroup().GetProperties().SetFill(fill)
156 self.__SendMessage(LAYER_LEGEND_CHANGED)
157
158 def GetDefaultFill(self):
159 """Return the default fill color."""
160 return self.GetDefaultGroup().GetProperties().GetFill()
161
162 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 self.__SendMessage(LAYER_LEGEND_CHANGED)
170
171 def GetDefaultLineColor(self):
172 """Return the default line color."""
173 return self.GetDefaultGroup().GetProperties().GetLineColor()
174
175 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 self.__SendMessage(LAYER_LEGEND_CHANGED)
183
184 def GetDefaultLineWidth(self):
185 """Return the default line width."""
186 return self.GetDefaultGroup().GetProperties().GetLineWidth()
187
188 def AddGroup(self, item):
189 """Add a new ClassGroup item to the classification.
190
191 item -- this must be a valid ClassGroup object
192 """
193
194 assert(isinstance(item, ClassGroup))
195
196 if len(self.groups) > 0 and isinstance(item, ClassGroupDefault):
197 self.groups[0] = item
198 #self.SetDefaultGroup(item)
199 else:
200 self.groups.append(item)
201
202 self.__SendMessage(LAYER_LEGEND_CHANGED)
203
204 def GetGroup(self, value):
205 """Return the associated group, or the default group.
206
207 Groups are checked in the order the were added to the
208 Classification.
209
210 value -- the value to classify. If there is no mapping,
211 or value is None, return the default properties
212 """
213
214 if self.field is not None and value is not None:
215
216 for i in range(1, len(self.groups)):
217 group = self.groups[i]
218 if group.Matches(value):
219 return group
220
221 return self.GetDefaultGroup()
222
223 def GetProperties(self, value):
224 """Return the properties associated with the given value."""
225
226 group = self.GetGroup(value)
227 if isinstance(group, ClassGroupMap):
228 return group.GetPropertiesFromValue(value)
229 else:
230 return group.GetProperties()
231
232 def TreeInfo(self):
233 items = []
234
235 def build_color_item(text, color):
236 if color is Color.None:
237 return ("%s: %s" % (text, _("None")), None)
238
239 return ("%s: (%.3f, %.3f, %.3f)" %
240 (text, color.red, color.green, color.blue),
241 color)
242
243 def build_item(group, string):
244 label = group.GetLabel()
245 if label == "":
246 label = string
247 else:
248 label += " (%s)" % string
249
250 props = group.GetProperties()
251 i = []
252 v = props.GetLineColor()
253 i.append(build_color_item(_("Line Color"), v))
254 v = props.GetLineWidth()
255 i.append(_("Line Width: %s") % v)
256 v = props.GetFill()
257 i.append(build_color_item(_("Fill"), v))
258 return (label, i)
259
260 for p in self:
261 if isinstance(p, ClassGroupDefault):
262 items.append(build_item(self.GetDefaultGroup(), _("'DEFAULT'")))
263 elif isinstance(p, ClassGroupSingleton):
264 items.append(build_item(p, str(p.GetValue())))
265 elif isinstance(p, ClassGroupRange):
266 items.append(build_item(p, "%s - %s" %
267 (p.GetMin(), p.GetMax())))
268
269 return (_("Classification"), items)
270
271 class ClassIterator:
272 """Allows the Groups in a Classifcation to be interated over.
273
274 The items are returned in the following order:
275 default data, singletons, ranges, maps
276 """
277
278 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 def __iter__(self):
296 return self
297
298 def next(self):
299 """Return the next item."""
300
301 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
321 class ClassGroupProperties:
322 """Represents the properties of a single Classification Group.
323
324 These are used when rendering a layer."""
325
326 def __init__(self, props = None):
327 """Constructor.
328
329 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 else:
341 self.SetLineColor(Color.None)
342 self.SetLineWidth(1)
343 self.SetFill(Color.None)
344
345 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 return self.stroke
356
357 def SetLineColor(self, color):
358 """Set the line color.
359
360 color -- the color of the line. This must be a Color object.
361 """
362
363 assert(isinstance(color, Color))
364 self.stroke = color
365
366 def GetLineWidth(self):
367 """Return the line width."""
368 return self.strokeWidth
369
370 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 def GetFill(self):
382 """Return the fill color as a Color object."""
383 return self.fill
384
385 def SetFill(self, fill):
386 """Set the fill color.
387
388 fill -- the color of the fill. This must be a Color object.
389 """
390
391 assert(isinstance(fill, Color))
392 self.fill = fill
393
394
395 class ClassGroup:
396 """A base class for all Groups within a Classification"""
397
398 def __init__(self, label = ""):
399 """Constructor.
400
401 label -- A string representing the Group's label
402 """
403
404 self.label = None
405
406 self.SetLabel(label)
407
408 def GetLabel(self):
409 """Return the Group's label."""
410 return self.label
411
412 def SetLabel(self, label):
413 """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 self.label = label
420
421 def Matches(self, value):
422 """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 pass
427
428 def GetProperties(self):
429 """Return the properties associated with the given value.
430
431 This needs to be implemented by all subclasses.
432 """
433 pass
434
435
436 class ClassGroupSingleton(ClassGroup):
437 """A Group that is associated with a single value."""
438
439 def __init__(self, value = 0, prop = None, label = ""):
440 """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 ClassGroup.__init__(self, label)
450
451 self.prop = None
452 self.value = None
453
454 self.SetValue(value)
455 self.SetProperties(prop)
456
457 def __copy__(self):
458 return ClassGroupSingleton(self.value, self.prop, self.label)
459
460 def GetValue(self):
461 """Return the associated value."""
462 return self.value
463
464 def SetValue(self, value):
465 """Associate this Group with the given value."""
466 self.value = value
467
468 def Matches(self, value):
469 """Determine if the given value matches the associated Group value."""
470
471 """Returns True if the value matches, False otherwise."""
472
473 return self.value == value
474
475 def GetProperties(self):
476 """Return the Properties associated with this Group."""
477
478 return self.prop
479
480 def SetProperties(self, prop):
481 """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 if prop is None: prop = ClassGroupProperties()
488 assert(isinstance(prop, ClassGroupProperties))
489 self.prop = prop
490
491
492 class ClassGroupDefault(ClassGroupSingleton):
493 """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 def __init__(self, prop = None, label = ""):
498 """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 ClassGroupSingleton.__init__(self, 0, prop, label)
507
508 def __copy__(self):
509 return ClassGroupDefault(self.prop, self.label)
510
511 def GetProperties(self):
512 """Return the Properties associated with this Group."""
513 return self.prop
514
515 class ClassGroupRange(ClassGroup):
516 """A Group that represents a range of values that map to the same
517 set of properties."""
518
519 def __init__(self, min = 0, max = 1, prop = None, label = ""):
520 """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 ClassGroup.__init__(self, label)
535
536 self.min = self.max = 0
537 self.prop = None
538
539 self.SetRange(min, max)
540 self.SetProperties(prop)
541
542 def __copy__(self):
543 return ClassGroupRange(self.min, self.max, self.prop, self.label)
544
545 def GetMin(self):
546 """Return the range's minimum value."""
547 return self.min
548
549 def SetMin(self, min):
550 """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 self.SetRange(min, self.max)
557
558 def GetMax(self):
559 """Return the range's maximum value."""
560 return self.max
561
562 def SetMax(self, max):
563 """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 self.SetRange(self.min, max)
569
570 def SetRange(self, min, max):
571 """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 if min >= max:
580 raise ValueError(_("ClassGroupRange: %i(min) >= %i(max)!") %
581 (min, max))
582 self.min = min
583 self.max = max
584
585 def GetRange(self):
586 """Return the range as a tuple (min, max)"""
587 return (self.min, self.max)
588
589 def Matches(self, value):
590 """Determine if the given value lies with the current range.
591
592 The following check is used: min <= value < max.
593 """
594
595 return self.min <= value < self.max
596
597 def GetProperties(self):
598 """Return the Properties associated with this Group."""
599 return self.prop
600
601 def SetProperties(self, prop):
602 """Set the properties associated with this Group.
603
604 prop -- a ClassGroupProperties object. if prop is None,
605 a default set of properties is created.
606 """
607 if prop is None: prop = ClassGroupProperties()
608 assert(isinstance(prop, ClassGroupProperties))
609 self.prop = prop
610
611 class ClassGroupMap(ClassGroup):
612 """Currently, this class is not used."""
613
614 FUNC_ID = "id"
615
616 def __init__(self, map_type = FUNC_ID, func = None, prop = None, label=""):
617 ClassGroup.__init__(self, label)
618
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 def GetProperties(self):
629 return None
630
631 def GetPropertiesFromValue(self, value):
632 pass
633
634 #
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