/[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 1843 - (show annotations)
Tue Oct 21 10:49:38 2003 UTC (21 years, 4 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/save.py
File MIME type: text/x-python
File size: 14070 byte(s)
(SessionSaver.write)
(SessionSaver.write_session): Use the 1.0 dtd and 1.0-dev
namespace
(SessionSaver.write_projection): Write the projection's epsg
attribute

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