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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1843 - (hide 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 bh 454 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 bh 6 # Authors:
3     # Jan-Oliver Wagner <[email protected]>
4     # Bernhard Herzog <[email protected]>
5 jonathan 414 # Jonathan Coles <[email protected]>
6 bh 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 bh 201 import Thuban.Lib.fileutil
19 bh 6
20 jonathan 932 from Thuban.Model.layer import Layer, RasterLayer
21    
22 jonathan 876 from Thuban.Model.classification import \
23     ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap
24 bh 1268 from Thuban.Model.transientdb import AutoTransientTable, TransientJoinedTable
25 bh 1417 from Thuban.Model.table import DBFTable, FIELDTYPE_STRING
26 bh 1268 from Thuban.Model.data import DerivedShapeStore, ShapefileStore
27 jonathan 429
28 jonathan 1160 from Thuban.Model.xmlwriter import XMLWriter
29 bh 1638 from postgisdb import PostGISConnection, PostGISShapeStore
30 jonathan 366
31 bh 201 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 bh 1268
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 jonathan 710 class SessionSaver(XMLWriter):
75 bh 268
76 jonathan 697 """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 jonathan 710 XMLWriter.__init__(self)
89 jonathan 697 self.session = session
90 bh 1268 # Map object ids to the ids used in the thuban files
91     self.idmap = {}
92 jonathan 697
93 bh 1268 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 jonathan 697 def write(self, file_or_filename):
107 jonathan 710 XMLWriter.write(self, file_or_filename)
108 jonathan 697
109 bh 1843 self.write_header("session", "thuban-1.0.dtd")
110 jonathan 697 self.write_session(self.session)
111     self.close()
112    
113 bh 268 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 bh 1268 # default name space
135     attrs["xmlns"] = \
136 bh 1843 "http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd"
137 jonathan 366 self.open_element("session", attrs)
138 bh 1638 self.write_db_connections(session)
139 bh 1268 self.write_data_containers(session)
140 bh 268 for map in session.Maps():
141     self.write_map(map)
142 jonathan 366 self.close_element("session")
143 bh 268
144 bh 1638 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 bh 1268 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 bh 1638 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 bh 1268 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 bh 1375 "leftcolumn": left_field,
208     "jointype": container.JoinType()})
209 bh 1268 else:
210     raise ValueError("Can't handle container %r" % container)
211    
212    
213 bh 268 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 jonathan 876 self.open_element('map title="%s"' % self.encode(map.title))
222 bh 268 self.write_projection(map.projection)
223     for layer in map.Layers():
224     self.write_layer(layer)
225     self.write_label_layer(map.LabelLayer())
226 jonathan 366 self.close_element('map')
227 bh 6
228 bh 268 def write_projection(self, projection):
229     """Write the projection.
230     """
231     if projection and len(projection.params) > 0:
232 bh 1843 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 bh 268 for param in projection.params:
238 jonathan 876 self.write_element('parameter value="%s"' %
239     self.encode(param))
240 jonathan 366 self.close_element("projection")
241 bh 268
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 jonathan 391
250 bh 268 if attrs is None:
251     attrs = {}
252 jonathan 429
253 jonathan 1251 attrs["title"] = layer.title
254     attrs["visible"] = ("false", "true")[int(layer.Visible())]
255 bh 268
256 jonathan 932 if isinstance(layer, Layer):
257 bh 1268 attrs["shapestore"] = self.get_id(layer.ShapeStore())
258 jonathan 740
259 jonathan 932 lc = layer.GetClassification()
260 bh 1268 attrs["stroke"] = lc.GetDefaultLineColor().hex()
261 jonathan 932 attrs["stroke_width"] = str(lc.GetDefaultLineWidth())
262 bh 1268 attrs["fill"] = lc.GetDefaultFill().hex()
263 jonathan 740
264 jonathan 932 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 bh 1219 attrs["filename"] = relative_filename(self.dir, layer.filename)
270 jonathan 932 self.open_element("rasterlayer", attrs)
271     self.write_projection(layer.GetProjection())
272     self.close_element("rasterlayer")
273    
274 jonathan 366 def write_classification(self, layer, attrs = None):
275 jonathan 1251 """Write Classification information."""
276    
277 jonathan 366 if attrs is None:
278     attrs = {}
279    
280 jonathan 414 lc = layer.GetClassification()
281 jonathan 366
282 bh 1452 field = layer.GetClassificationColumn()
283    
284 jonathan 366 #
285 jonathan 1251 # there isn't a classification of anything so do nothing
286 jonathan 366 #
287     if field is None: return
288    
289     attrs["field"] = field
290 jonathan 1430 attrs["field_type"] = str(layer.GetFieldType(field))
291 jonathan 366 self.open_element("classification", attrs)
292    
293 jonathan 1251 for g in lc:
294     if isinstance(g, ClassGroupDefault):
295     open_el = 'clnull label="%s"' % self.encode(g.GetLabel())
296     close_el = 'clnull'
297 bh 1417 elif isinstance(g, ClassGroupSingleton):
298 jonathan 1430 if layer.GetFieldType(field) == FIELDTYPE_STRING:
299 bh 1417 value = self.encode(g.GetValue())
300     else:
301     value = str(g.GetValue())
302 jonathan 1251 open_el = 'clpoint label="%s" value="%s"' \
303 bh 1417 % (self.encode(g.GetLabel()), value)
304 jonathan 1251 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 jonathan 429
313 jonathan 1251 data = g.GetProperties()
314     dict = {'stroke' : data.GetLineColor().hex(),
315     'stroke_width': str(data.GetLineWidth()),
316     'fill' : data.GetFill().hex()}
317 jonathan 429
318 jonathan 1251 self.open_element(open_el)
319     self.write_element("cldata", dict)
320     self.close_element(close_el)
321 jonathan 366
322     self.close_element("classification")
323    
324 bh 268 def write_label_layer(self, layer):
325     """Write the label layer.
326     """
327     labels = layer.Labels()
328 bh 6 if labels:
329 jonathan 366 self.open_element('labellayer')
330 bh 6 for label in labels:
331 jonathan 366 self.write_element(('label x="%g" y="%g" text="%s"'
332     ' halign="%s" valign="%s"')
333 jonathan 876 % (label.x, label.y,
334     self.encode(label.text),
335     label.halign,
336 bh 268 label.valign))
337 jonathan 366 self.close_element('labellayer')
338 bh 6
339 bh 268
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 jonathan 697 SessionSaver.
349 bh 268
350     If writing the session is successful call the session's
351     UnsetModified method
352     """
353     if saver_class is None:
354 jonathan 697 saver_class = SessionSaver
355 bh 268 saver = saver_class(session)
356     saver.write(file)
357    
358 bh 6 # 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