/[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 1340 - (show annotations)
Tue Jul 1 16:10:25 2003 UTC (21 years, 8 months ago) by jonathan
Original Path: trunk/thuban/Thuban/Model/save.py
File MIME type: text/x-python
File size: 12560 byte(s)
Removed import of Color which wasn't being used.

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
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.8.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.8.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 else:
188 raise ValueError("Can't handle container %r" % container)
189
190
191 def write_map(self, map):
192 """Write the map and its contents.
193
194 By default, write a map element element with the title
195 attribute, call write_projection to write the projection
196 element, call write_layer for each layer contained in the map
197 and finally call write_label_layer to write the label layer.
198 """
199 self.open_element('map title="%s"' % self.encode(map.title))
200 self.write_projection(map.projection)
201 for layer in map.Layers():
202 self.write_layer(layer)
203 self.write_label_layer(map.LabelLayer())
204 self.close_element('map')
205
206 def write_projection(self, projection):
207 """Write the projection.
208 """
209 if projection and len(projection.params) > 0:
210 self.open_element("projection", {"name": projection.GetName()})
211 for param in projection.params:
212 self.write_element('parameter value="%s"' %
213 self.encode(param))
214 self.close_element("projection")
215
216 def write_layer(self, layer, attrs = None):
217 """Write the layer.
218
219 The optional argument attrs is for additional attributes and, if
220 given, should be a mapping from attribute names to attribute
221 values. The values should not be XML-escaped yet.
222 """
223
224 if attrs is None:
225 attrs = {}
226
227 attrs["title"] = layer.title
228 attrs["visible"] = ("false", "true")[int(layer.Visible())]
229
230 if isinstance(layer, Layer):
231 attrs["shapestore"] = self.get_id(layer.ShapeStore())
232
233 lc = layer.GetClassification()
234 attrs["stroke"] = lc.GetDefaultLineColor().hex()
235 attrs["stroke_width"] = str(lc.GetDefaultLineWidth())
236 attrs["fill"] = lc.GetDefaultFill().hex()
237
238 self.open_element("layer", attrs)
239 self.write_projection(layer.GetProjection())
240 self.write_classification(layer)
241 self.close_element("layer")
242 elif isinstance(layer, RasterLayer):
243 attrs["filename"] = relative_filename(self.dir, layer.filename)
244 self.open_element("rasterlayer", attrs)
245 self.write_projection(layer.GetProjection())
246 self.close_element("rasterlayer")
247
248 def write_classification(self, layer, attrs = None):
249 """Write Classification information."""
250
251 if attrs is None:
252 attrs = {}
253
254 lc = layer.GetClassification()
255
256 field = lc.GetField()
257
258 #
259 # there isn't a classification of anything so do nothing
260 #
261 if field is None: return
262
263 attrs["field"] = field
264 attrs["field_type"] = str(lc.GetFieldType())
265 self.open_element("classification", attrs)
266
267 for g in lc:
268 if isinstance(g, ClassGroupDefault):
269 open_el = 'clnull label="%s"' % self.encode(g.GetLabel())
270 close_el = 'clnull'
271 elif isinstance(g, ClassGroupSingleton):
272 open_el = 'clpoint label="%s" value="%s"' \
273 % (self.encode(g.GetLabel()), str(g.GetValue()))
274 close_el = 'clpoint'
275 elif isinstance(g, ClassGroupRange):
276 open_el = 'clrange label="%s" range="%s"' \
277 % (self.encode(g.GetLabel()), str(g.GetRange()))
278 close_el = 'clrange'
279 else:
280 assert False, _("Unsupported group type in classification")
281 continue
282
283 data = g.GetProperties()
284 dict = {'stroke' : data.GetLineColor().hex(),
285 'stroke_width': str(data.GetLineWidth()),
286 'fill' : data.GetFill().hex()}
287
288 self.open_element(open_el)
289 self.write_element("cldata", dict)
290 self.close_element(close_el)
291
292 self.close_element("classification")
293
294 def write_label_layer(self, layer):
295 """Write the label layer.
296 """
297 labels = layer.Labels()
298 if labels:
299 self.open_element('labellayer')
300 for label in labels:
301 self.write_element(('label x="%g" y="%g" text="%s"'
302 ' halign="%s" valign="%s"')
303 % (label.x, label.y,
304 self.encode(label.text),
305 label.halign,
306 label.valign))
307 self.close_element('labellayer')
308
309
310
311 def save_session(session, file, saver_class = None):
312 """Save the session session to a file.
313
314 The file argument may either be a filename or an open file object.
315
316 The optional argument saver_class is the class to use to serialize
317 the session. By default or if it's None, the saver class will be
318 SessionSaver.
319
320 If writing the session is successful call the session's
321 UnsetModified method
322 """
323 if saver_class is None:
324 saver_class = SessionSaver
325 saver = saver_class(session)
326 saver.write(file)
327
328 # after a successful save consider the session unmodified.
329 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