/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/save.py
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/Thuban/Model/save.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1452 - (show annotations)
Fri Jul 18 12:57:59 2003 UTC (21 years, 7 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/save.py
File MIME type: text/x-python
File size: 12852 byte(s)
* Thuban/Model/layer.py (Layer.__init__): Rename
classificationField to classificatin_column and init it here so
that it can be used in SetClassificationColumn
(Layer.GetClassificationColumn, Layer.GetClassificationField):
Rename to GetClassificationColumn.
(Layer.SetClassificationColumn, Layer.SetClassificationField):
Rename to SetClassificationColumn and issue a LAYER_CHANGED
message if the column changes.
(Layer._classification_changed, Layer.ClassChanged): Rename to
_classification_changed. Update the callers.
(Layer.SetShapeStore): Further field->column renames.

* Thuban/Model/load.py (SessionLoader.start_classification)
(SessionLoader.start_clpoint): Updates because of
field->column method name changes in the Layer class

* Thuban/Model/save.py (SessionSaver.write_classification): Updates
because of field->column method name changes in the Layer class

* Thuban/UI/classifier.py (Classifier.__init__)
(Classifier._OnTry, Classifier._OnRevert): Updates because of
field->column method name changes in the Layer class

* Thuban/UI/renderer.py (MapRenderer.draw_shape_layer): Updates
because of field->column method name changes in the Layer class

* Thuban/UI/viewport.py (ViewPort.find_shape_at): Updates because
of field->column method name changes in the Layer class

* test/test_save.py (SaveSessionTest.testClassifiedLayer)
(SaveSessionTest.testClassifiedLayer): Update because of
field->column method name changes in the Layer class

* test/test_layer.py (SetShapeStoreTests.setUp)
(SetShapeStoreTests.test_sanity): Update because of field->column
method name changes in the Layer class
(TestLayerModification.setUp): Subscribe to LAYER_CHANGED as well
(TestLayerModification.test_sanity)
(TestLayerModification.test_initial_settings): remove unsued code
and rename to test_sanity.
(TestLayerModification.test_set_classification): New test for
SetClassification and SetClassificationField.

1 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Jan-Oliver Wagner <[email protected]>
4 # Bernhard Herzog <[email protected]>
5 # Jonathan Coles <[email protected]>
6 #
7 # This program is free software under the GPL (>=v2)
8 # Read the file COPYING coming with Thuban for details.
9
10 """
11 Functions to save a session to a file
12 """
13
14 __version__ = "$Revision$"
15
16 import os
17
18 import Thuban.Lib.fileutil
19
20 from Thuban.Model.layer import Layer, RasterLayer
21
22 from Thuban.Model.classification import \
23 ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap
24 from Thuban.Model.transientdb import AutoTransientTable, TransientJoinedTable
25 from Thuban.Model.table import DBFTable, FIELDTYPE_STRING
26 from Thuban.Model.data import DerivedShapeStore, ShapefileStore
27
28 from Thuban.Model.xmlwriter import XMLWriter
29
30 def relative_filename(dir, filename):
31 """Return a filename relative to dir for the absolute file name absname.
32
33 This is almost the same as the function in fileutil, except that dir
34 can be an empty string in which case filename will be returned
35 unchanged.
36 """
37 if dir:
38 return Thuban.Lib.fileutil.relative_filename(dir, filename)
39 else:
40 return filename
41
42
43 def sort_data_stores(stores):
44 """Return a topologically sorted version of the sequence of data containers
45
46 The list is sorted so that data containers that depend on other data
47 containers have higher indexes than the containers they depend on.
48 """
49 if not stores:
50 return []
51 processed = {}
52 result = []
53 todo = stores[:]
54 while todo:
55 # It doesn't really matter which if the items of todo is
56 # processed next, but if we take the first one, the order is
57 # preserved to some degree which makes writing some of the test
58 # cases easier.
59 container = todo.pop(0)
60 if id(container) in processed:
61 continue
62 deps = [dep for dep in container.Dependencies()
63 if id(dep) not in processed]
64 if deps:
65 todo.append(container)
66 todo.extend(deps)
67 else:
68 result.append(container)
69 processed[id(container)] = 1
70 return result
71
72
73 class SessionSaver(XMLWriter):
74
75 """Class to serialize a session into an XML file.
76
77 Applications built on top of Thuban may derive from this class and
78 override or extend the methods to save additional information. This
79 additional information should take the form of additional attributes
80 or elements whose names are prefixed with a namespace. To define a
81 namespace derived classes should extend the write_session method to
82 pass the namespaces to the default implementation.
83 """
84
85
86 def __init__(self, session):
87 XMLWriter.__init__(self)
88 self.session = session
89 # Map object ids to the ids used in the thuban files
90 self.idmap = {}
91
92 def get_id(self, obj):
93 """Return the id used in the thuban file for the object obj"""
94 return self.idmap.get(id(obj))
95
96 def define_id(self, obj, value = None):
97 if value is None:
98 value = "D" + str(id(obj))
99 self.idmap[id(obj)] = value
100 return value
101
102 def has_id(self, obj):
103 return self.idmap.has_key(id(obj))
104
105 def write(self, file_or_filename):
106 XMLWriter.write(self, file_or_filename)
107
108 self.write_header("session", "thuban-0.9.dtd")
109 self.write_session(self.session)
110 self.close()
111
112 def write_session(self, session, attrs = None, namespaces = ()):
113 """Write the session and its contents
114
115 By default, write a session element with the title attribute and
116 call write_map for each map contained in the session.
117
118 The optional argument attrs is for additional attributes and, if
119 given, should be a mapping from attribute names to attribute
120 values. The values should not be XML-escaped yet.
121
122 The optional argument namespaces, if given, should be a sequence
123 of (name, URI) pairs. The namespaces are written as namespace
124 attributes into the session element. This is mainly useful for
125 derived classes that need to store additional information in a
126 thuban session file.
127 """
128 if attrs is None:
129 attrs = {}
130 attrs["title"] = session.title
131 for name, uri in namespaces:
132 attrs["xmlns:" + name] = uri
133 # default name space
134 attrs["xmlns"] = \
135 "http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd"
136 self.open_element("session", attrs)
137 self.write_data_containers(session)
138 for map in session.Maps():
139 self.write_map(map)
140 self.close_element("session")
141
142 def write_data_containers(self, session):
143 containers = sort_data_stores(session.DataContainers())
144 for container in containers:
145 if isinstance(container, AutoTransientTable):
146 # AutoTransientTable instances are invisible in the
147 # thuban files. They're only used internally. To make
148 # sure that containers depending on AutoTransientTable
149 # instances refer to the right real containers we give
150 # the AutoTransientTable instances the same id as the
151 # source they depend on.
152 self.define_id(container,
153 self.get_id(container.Dependencies()[0]))
154 continue
155
156 idvalue = self.define_id(container)
157 if isinstance(container, ShapefileStore):
158 self.define_id(container.Table(), idvalue)
159 filename = relative_filename(self.dir, container.FileName())
160 self.write_element("fileshapesource",
161 {"id": idvalue, "filename": filename,
162 "filetype": "shapefile"})
163 elif isinstance(container, DerivedShapeStore):
164 shapesource, table = container.Dependencies()
165 self.write_element("derivedshapesource",
166 {"id": idvalue,
167 "shapesource": self.get_id(shapesource),
168 "table": self.get_id(table)})
169 elif isinstance(container, DBFTable):
170 filename = relative_filename(self.dir, container.FileName())
171 self.write_element("filetable",
172 {"id": idvalue,
173 "title": container.Title(),
174 "filename": filename,
175 "filetype": "DBF"})
176 elif isinstance(container, TransientJoinedTable):
177 left, right = container.Dependencies()
178 left_field = container.left_field
179 right_field = container.right_field
180 self.write_element("jointable",
181 {"id": idvalue,
182 "title": container.Title(),
183 "right": self.get_id(right),
184 "rightcolumn": right_field,
185 "left": self.get_id(left),
186 "leftcolumn": left_field,
187 "jointype": container.JoinType()})
188 else:
189 raise ValueError("Can't handle container %r" % container)
190
191
192 def write_map(self, map):
193 """Write the map and its contents.
194
195 By default, write a map element element with the title
196 attribute, call write_projection to write the projection
197 element, call write_layer for each layer contained in the map
198 and finally call write_label_layer to write the label layer.
199 """
200 self.open_element('map title="%s"' % self.encode(map.title))
201 self.write_projection(map.projection)
202 for layer in map.Layers():
203 self.write_layer(layer)
204 self.write_label_layer(map.LabelLayer())
205 self.close_element('map')
206
207 def write_projection(self, projection):
208 """Write the projection.
209 """
210 if projection and len(projection.params) > 0:
211 self.open_element("projection", {"name": projection.GetName()})
212 for param in projection.params:
213 self.write_element('parameter value="%s"' %
214 self.encode(param))
215 self.close_element("projection")
216
217 def write_layer(self, layer, attrs = None):
218 """Write the layer.
219
220 The optional argument attrs is for additional attributes and, if
221 given, should be a mapping from attribute names to attribute
222 values. The values should not be XML-escaped yet.
223 """
224
225 if attrs is None:
226 attrs = {}
227
228 attrs["title"] = layer.title
229 attrs["visible"] = ("false", "true")[int(layer.Visible())]
230
231 if isinstance(layer, Layer):
232 attrs["shapestore"] = self.get_id(layer.ShapeStore())
233
234 lc = layer.GetClassification()
235 attrs["stroke"] = lc.GetDefaultLineColor().hex()
236 attrs["stroke_width"] = str(lc.GetDefaultLineWidth())
237 attrs["fill"] = lc.GetDefaultFill().hex()
238
239 self.open_element("layer", attrs)
240 self.write_projection(layer.GetProjection())
241 self.write_classification(layer)
242 self.close_element("layer")
243 elif isinstance(layer, RasterLayer):
244 attrs["filename"] = relative_filename(self.dir, layer.filename)
245 self.open_element("rasterlayer", attrs)
246 self.write_projection(layer.GetProjection())
247 self.close_element("rasterlayer")
248
249 def write_classification(self, layer, attrs = None):
250 """Write Classification information."""
251
252 if attrs is None:
253 attrs = {}
254
255 lc = layer.GetClassification()
256
257 field = layer.GetClassificationColumn()
258
259 #
260 # there isn't a classification of anything so do nothing
261 #
262 if field is None: return
263
264 attrs["field"] = field
265 attrs["field_type"] = str(layer.GetFieldType(field))
266 self.open_element("classification", attrs)
267
268 for g in lc:
269 if isinstance(g, ClassGroupDefault):
270 open_el = 'clnull label="%s"' % self.encode(g.GetLabel())
271 close_el = 'clnull'
272 elif isinstance(g, ClassGroupSingleton):
273 if layer.GetFieldType(field) == FIELDTYPE_STRING:
274 value = self.encode(g.GetValue())
275 else:
276 value = str(g.GetValue())
277 open_el = 'clpoint label="%s" value="%s"' \
278 % (self.encode(g.GetLabel()), value)
279 close_el = 'clpoint'
280 elif isinstance(g, ClassGroupRange):
281 open_el = 'clrange label="%s" range="%s"' \
282 % (self.encode(g.GetLabel()), str(g.GetRange()))
283 close_el = 'clrange'
284 else:
285 assert False, _("Unsupported group type in classification")
286 continue
287
288 data = g.GetProperties()
289 dict = {'stroke' : data.GetLineColor().hex(),
290 'stroke_width': str(data.GetLineWidth()),
291 'fill' : data.GetFill().hex()}
292
293 self.open_element(open_el)
294 self.write_element("cldata", dict)
295 self.close_element(close_el)
296
297 self.close_element("classification")
298
299 def write_label_layer(self, layer):
300 """Write the label layer.
301 """
302 labels = layer.Labels()
303 if labels:
304 self.open_element('labellayer')
305 for label in labels:
306 self.write_element(('label x="%g" y="%g" text="%s"'
307 ' halign="%s" valign="%s"')
308 % (label.x, label.y,
309 self.encode(label.text),
310 label.halign,
311 label.valign))
312 self.close_element('labellayer')
313
314
315
316 def save_session(session, file, saver_class = None):
317 """Save the session session to a file.
318
319 The file argument may either be a filename or an open file object.
320
321 The optional argument saver_class is the class to use to serialize
322 the session. By default or if it's None, the saver class will be
323 SessionSaver.
324
325 If writing the session is successful call the session's
326 UnsetModified method
327 """
328 if saver_class is None:
329 saver_class = SessionSaver
330 saver = saver_class(session)
331 saver.write(file)
332
333 # after a successful save consider the session unmodified.
334 session.UnsetModified()

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26