/[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 436 - (show annotations)
Thu Feb 27 15:53:03 2003 UTC (22 years ago) by jonathan
Original Path: trunk/thuban/Thuban/Model/classification.py
File MIME type: text/x-python
File size: 12356 byte(s)
 Changed the class hierarchy
        so that a Classification consists of Groups which return
        Properties when a value matches a Group.

1 # Copyright (c) 2001 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 GetClassData() 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 from wxPython.wx import *
37
38 # constants
39 RANGE_MIN = 0
40 RANGE_MAX = 1
41 RANGE_DATA = 2
42
43 class Classification:
44
45 def __init__(self, layer = None, field = None):
46 """Initialize a classification.
47
48 layer -- the layer object who owns this classification
49
50 field -- the name of the data table field that
51 is to be used to classify layer properties
52 """
53
54 self.layer = None # stop message sending
55
56 self.SetDefaultGroup(ClassGroupDefault())
57 self.SetField(field)
58
59 self.layer = layer
60 self.points = []
61 self.ranges = []
62 self.maps = []
63
64 def __iter__(self):
65 return ClassIterator(self.DefaultGroup,
66 self.points,
67 self.ranges,
68 self.maps)
69
70 def __SendMessage(self, message):
71 if self.layer is not None:
72 self.layer.changed(message, self.layer)
73
74 def SetField(self, field):
75 """Set the name of the data table field to use.
76
77 field -- if None then all values map to the default data
78 """
79
80 if field == "":
81 field = None
82
83 self.field = field
84 self.__SendMessage(LAYER_LEGEND_CHANGED)
85
86 def GetField(self):
87 return self.field
88
89 def SetLayer(self, layer):
90 assert(isinstance(layer, Thuban.Model.layer.Layer))
91 self.layer = layer
92 self.__SendMessage(LAYER_LEGEND_CHANGED)
93
94 def GetLayer(self):
95 return layer.self
96
97 def SetDefaultGroup(self, group):
98 """Set the group to be used when a value can't be classified.
99
100 group -- group that the value maps to. See class description.
101 """
102
103 assert(isinstance(group, ClassGroupDefault))
104 self.DefaultGroup = group
105
106 def GetDefaultGroup(self):
107 return self.DefaultGroup
108
109 #
110 # these SetDefault* methods are really only provided for
111 # some backward compatibility. they should be considered
112 # for removal once all the classification code is finished.
113 #
114
115 def SetDefaultFill(self, fill):
116 assert(isinstance(fill, Color))
117 self.DefaultGroup.GetProperties().SetFill(fill)
118 self.__SendMessage(LAYER_LEGEND_CHANGED)
119
120 def GetDefaultFill(self):
121 return self.DefaultGroup.GetProperties().GetFill()
122
123 def SetDefaultStroke(self, stroke):
124 assert(isinstance(stroke, Color))
125 self.DefaultGroup.GetProperties().SetStroke(stroke)
126 self.__SendMessage(LAYER_LEGEND_CHANGED)
127
128 def GetDefaultStroke(self):
129 return self.DefaultGroup.GetProperties().GetStroke()
130
131 def SetDefaultStrokeWidth(self, strokeWidth):
132 assert(isinstance(strokeWidth, IntType))
133 self.DefaultGroup.GetProperties().SetStrokeWidth(strokeWidth)
134 self.__SendMessage(LAYER_LEGEND_CHANGED)
135
136 def GetDefaultStrokeWidth(self):
137 return self.DefaultGroup.GetProperties().GetStrokeWidth()
138
139 def AddGroup(self, item):
140 assert(isinstance(item, ClassGroup))
141
142 if isinstance(item, ClassGroupDefault):
143 self.SetDefaultGroup(item)
144 elif isinstance(item, ClassGroupSingleton):
145 self.points.append(item)
146 elif isinstance(item, ClassGroupRange):
147 self.ranges.append(item)
148 elif isinstance(item, ClassGroupMap):
149 self.maps.append(item)
150 else:
151 raise ValueError(_("Unrecognized ClassGroup"))
152
153 self.__SendMessage(LAYER_LEGEND_CHANGED)
154
155 def GetGroup(self, value):
156 """Return the associated data, or the default data.
157
158 The following search technique is used:
159 (1) if the field is None, return the default data
160 (2) check if the value exists as a single value
161 (3) check if the value falls within a range. Ranges
162 are checked in the order they were added to
163 the classification.
164
165 value -- the value to classify. If there is no mapping,
166 or value is None, return the default properties
167 """
168
169 if self.field is not None and value is not None:
170
171 for p in self:
172 if p.Matches(value):
173 return p
174 # #
175 # # check the discrete values
176 # #
177 # if self.points.has_key(value):
178 # return self.points[value]
179 # #for p in self.points:
180 # #if p.Value
181
182 # #
183 # # check the ranges
184 # #
185 # for p in self.ranges:
186 # if p.InRange(value):
187 # return p
188
189 # #
190 # # check the maps
191 # #
192 # for p in self.maps:
193 # try:
194 # return p.Map(value)
195 # except: pass
196
197 return self.DefaultGroup
198
199 def GetProperties(self, value):
200 return self.GetGroup(value).GetProperties()
201
202 def TreeInfo(self):
203 items = []
204
205 def build_color_item(text, color):
206 if color is Color.None:
207 return ("%s: %s" % (text, _("None")), None)
208
209 return ("%s: (%.3f, %.3f, %.3f)" %
210 (text, color.red, color.green, color.blue),
211 color)
212
213 def build_item(group, string):
214 label = group.GetLabel()
215 if label == "":
216 label = string
217 else:
218 label += " (%s)" % string
219
220 props = group.GetProperties()
221 i = []
222 v = props.GetStroke()
223 i.append(build_color_item(_("Stroke"), v))
224 v = props.GetStrokeWidth()
225 i.append(_("Stroke Width: %s") % v)
226 v = props.GetFill()
227 i.append(build_color_item(_("Fill"), v))
228 return (label, i)
229
230 for p in self:
231 if isinstance(p, ClassGroupDefault):
232 items.append(build_item(self.DefaultGroup, _("'DEFAULT'")))
233 elif isinstance(p, ClassGroupSingleton):
234 items.append(build_item(p, str(p.GetValue())))
235 elif isinstance(p, ClassGroupRange):
236 items.append(build_item(p, "%s - %s" %
237 (p.GetMin(), p.GetMax())))
238
239 # for p in self.points.values():
240 # items.append(build_item(p, str(p.GetValue())))
241
242 # for p in self.ranges:
243 # items.append(build_item(p, "%s - %s" % (p.GetMin(), p.GetMax())))
244
245 return (_("Classification"), items)
246
247 class ClassIterator:
248
249 def __init__(self, default, points, ranges, maps):
250 self.data = [default, points, ranges, maps]
251 self.data_iter = iter(self.data)
252 self.iter = None
253
254 def __iter__(self):
255 return self
256
257 def next(self):
258 if self.iter is None:
259 try:
260 self.data_item = self.data_iter.next()
261 self.iter = iter(self.data_item)
262 except TypeError:
263 return self.data_item
264
265 try:
266 return self.iter.next()
267 except StopIteration:
268 self.iter = None
269 return self.next()
270
271 class ClassGroupProperties:
272
273 def __init__(self, prop = None):
274
275 if prop is not None:
276 self.SetStroke(prop.GetStroke())
277 self.SetStrokeWidth(prop.GetStrokeWidth())
278 self.SetFill(prop.GetFill())
279 else:
280 self.SetStroke(Color.None)
281 self.SetStrokeWidth(1)
282 self.SetFill(Color.None)
283
284 def GetStroke(self):
285 return self.stroke
286
287 def SetStroke(self, stroke):
288 assert(isinstance(stroke, Color))
289 self.stroke = stroke
290
291 def GetStrokeWidth(self):
292 return self.stroke_width
293
294 def SetStrokeWidth(self, stroke_width):
295 assert(isinstance(stroke_width, IntType))
296 if (stroke_width < 1):
297 raise ValueError(_("stroke_width < 1"))
298
299 self.stroke_width = stroke_width
300
301 def GetFill(self):
302 return self.fill
303
304 def SetFill(self, fill):
305 assert(isinstance(fill, Color))
306 self.fill = fill
307
308
309 class ClassGroup:
310
311 def __init__(self, label = ""):
312 self.label = label
313
314 def GetLabel(self):
315 return self.label
316
317 def SetLabel(self, label):
318 self.label = label
319
320 def Matches(self, value):
321 """This needs to be implemented by all subclasses."""
322 pass
323
324 def GetProperties(self, value):
325 """This needs to be implemented by all subclasses."""
326 pass
327
328
329 class ClassGroupSingleton(ClassGroup):
330
331 def __init__(self, value = 0, prop = None, label = ""):
332 ClassGroup.__init__(self, label)
333
334 self.SetValue(value)
335 self.SetProperties(prop)
336
337 def __copy__(self):
338 return ClassGroupSingleton(self.value, self.prop, self.label)
339
340 def GetValue(self):
341 return self.value
342
343 def SetValue(self, value):
344 self.value = value
345
346 def Matches(self, value):
347 return self.value == value
348
349 def GetProperties(self, value = None):
350 if value is None: return self.prop
351
352 if self.Matches(value):
353 return self.prop
354 else:
355 return None
356
357 def SetProperties(self, prop):
358 if prop is None: prop = ClassGroupProperties()
359 assert(isinstance(prop, ClassGroupProperties))
360 self.prop = prop
361
362
363 class ClassGroupDefault(ClassGroupSingleton):
364 def __init__(self, prop = None, label = ""):
365 ClassGroupSingleton.__init__(self, 0, prop, label)
366
367 def __copy__(self):
368 return ClassGroupDefault(self.prop, self.label)
369
370 def GetProperties(self, value = None):
371 return self.prop
372
373 class ClassGroupRange(ClassGroup):
374
375 def __init__(self, min = 0, max = 1, prop = None, label = ""):
376 ClassGroup.__init__(self, label)
377
378 self.SetRange(min, max)
379 self.SetProperties(prop)
380
381 def __copy__(self):
382 return ClassGroupRange(self.min, self.max, self.prop, self.label)
383
384 def GetMin(self):
385 return self.min
386
387 def SetMin(self, min):
388 self.SetRange(min, self.max)
389
390 def GetMax(self):
391 return self.max
392
393 def SetMax(self, max):
394 self.SetRange(self.min, max)
395
396 def SetRange(self, min, max):
397 if min >= max:
398 raise ValueError(_("ClassGroupRange: %i(min) >= %i(max)!") %
399 (min, max))
400 self.min = min
401 self.max = max
402
403 def GetRange(self):
404 return (self.min, self.max)
405
406 def Matches(self, value):
407 return self.min <= value < self.max
408
409 def GetProperties(self, value):
410 if value is None: return self.prop
411
412 if self.Matches(value):
413 return self.prop
414 else:
415 return None
416
417 def SetProperties(self, prop):
418 if prop is None: prop = ClassGroupProperties()
419 assert(isinstance(prop, ClassGroupProperties))
420 self.prop = prop
421
422 class ClassGroupMap(ClassGroup):
423
424 FUNC_ID = "id"
425
426 def __init__(self, map_type = FUNC_ID, func = None, prop = None, label=""):
427 ClassGroup.__init__(self, prop)
428
429 self.map_type = map_type
430 self.func = func
431
432 if self.func is None:
433 self.func = func_id
434
435 def Map(self, value):
436 return self.func(value)
437
438 #
439 # built-in mappings
440 #
441 def func_id(value):
442 return value
443

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26