/[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 453 - (show annotations)
Tue Mar 4 11:31:39 2003 UTC (22 years ago) by bh
Original Path: trunk/thuban/Thuban/Model/classification.py
File MIME type: text/x-python
File size: 12339 byte(s)
Remove unnecessary wxPython import

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26