/[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 428 - (show annotations)
Mon Feb 24 18:46:35 2003 UTC (22 years ago) by jonathan
Original Path: trunk/thuban/Thuban/Model/classification.py
File MIME type: text/x-python
File size: 10436 byte(s)
(Classification): Renamed
        GetProperties() to GetClassData(). Used the new iterator
        in TreeInfo().
(ClassIterator): Iterator implementation to iterate over the
        ClassData objects in a classification object.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26