/[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 1340 - (hide 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 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     from Thuban.Model.table import DBFTable
26     from Thuban.Model.data import DerivedShapeStore, ShapefileStore
27 jonathan 429
28 jonathan 1160 from Thuban.Model.xmlwriter import XMLWriter
29 jonathan 366
30 bh 201 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 bh 1268
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 jonathan 710 class SessionSaver(XMLWriter):
74 bh 268
75 jonathan 697 """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 jonathan 710 XMLWriter.__init__(self)
88 jonathan 697 self.session = session
89 bh 1268 # Map object ids to the ids used in the thuban files
90     self.idmap = {}
91 jonathan 697
92 bh 1268 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 jonathan 697 def write(self, file_or_filename):
106 jonathan 710 XMLWriter.write(self, file_or_filename)
107 jonathan 697
108 bh 1268 self.write_header("session", "thuban-0.8.dtd")
109 jonathan 697 self.write_session(self.session)
110     self.close()
111    
112 bh 268 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 bh 1268 # default name space
134     attrs["xmlns"] = \
135     "http://thuban.intevation.org/dtds/thuban-0.8.dtd"
136 jonathan 366 self.open_element("session", attrs)
137 bh 1268 self.write_data_containers(session)
138 bh 268 for map in session.Maps():
139     self.write_map(map)
140 jonathan 366 self.close_element("session")
141 bh 268
142 bh 1268 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 bh 268 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 jonathan 876 self.open_element('map title="%s"' % self.encode(map.title))
200 bh 268 self.write_projection(map.projection)
201     for layer in map.Layers():
202     self.write_layer(layer)
203     self.write_label_layer(map.LabelLayer())
204 jonathan 366 self.close_element('map')
205 bh 6
206 bh 268 def write_projection(self, projection):
207     """Write the projection.
208     """
209     if projection and len(projection.params) > 0:
210 jonathan 876 self.open_element("projection", {"name": projection.GetName()})
211 bh 268 for param in projection.params:
212 jonathan 876 self.write_element('parameter value="%s"' %
213     self.encode(param))
214 jonathan 366 self.close_element("projection")
215 bh 268
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 jonathan 391
224 bh 268 if attrs is None:
225     attrs = {}
226 jonathan 429
227 jonathan 1251 attrs["title"] = layer.title
228     attrs["visible"] = ("false", "true")[int(layer.Visible())]
229 bh 268
230 jonathan 932 if isinstance(layer, Layer):
231 bh 1268 attrs["shapestore"] = self.get_id(layer.ShapeStore())
232 jonathan 740
233 jonathan 932 lc = layer.GetClassification()
234 bh 1268 attrs["stroke"] = lc.GetDefaultLineColor().hex()
235 jonathan 932 attrs["stroke_width"] = str(lc.GetDefaultLineWidth())
236 bh 1268 attrs["fill"] = lc.GetDefaultFill().hex()
237 jonathan 740
238 jonathan 932 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 bh 1219 attrs["filename"] = relative_filename(self.dir, layer.filename)
244 jonathan 932 self.open_element("rasterlayer", attrs)
245     self.write_projection(layer.GetProjection())
246     self.close_element("rasterlayer")
247    
248 jonathan 366 def write_classification(self, layer, attrs = None):
249 jonathan 1251 """Write Classification information."""
250    
251 jonathan 366 if attrs is None:
252     attrs = {}
253    
254 jonathan 414 lc = layer.GetClassification()
255 jonathan 366
256 jonathan 429 field = lc.GetField()
257 jonathan 366
258     #
259 jonathan 1251 # there isn't a classification of anything so do nothing
260 jonathan 366 #
261     if field is None: return
262    
263     attrs["field"] = field
264 jonathan 466 attrs["field_type"] = str(lc.GetFieldType())
265 jonathan 366 self.open_element("classification", attrs)
266    
267 jonathan 1251 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 jonathan 429
283 jonathan 1251 data = g.GetProperties()
284     dict = {'stroke' : data.GetLineColor().hex(),
285     'stroke_width': str(data.GetLineWidth()),
286     'fill' : data.GetFill().hex()}
287 jonathan 429
288 jonathan 1251 self.open_element(open_el)
289     self.write_element("cldata", dict)
290     self.close_element(close_el)
291 jonathan 366
292     self.close_element("classification")
293    
294 bh 268 def write_label_layer(self, layer):
295     """Write the label layer.
296     """
297     labels = layer.Labels()
298 bh 6 if labels:
299 jonathan 366 self.open_element('labellayer')
300 bh 6 for label in labels:
301 jonathan 366 self.write_element(('label x="%g" y="%g" text="%s"'
302     ' halign="%s" valign="%s"')
303 jonathan 876 % (label.x, label.y,
304     self.encode(label.text),
305     label.halign,
306 bh 268 label.valign))
307 jonathan 366 self.close_element('labellayer')
308 bh 6
309 bh 268
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 jonathan 697 SessionSaver.
319 bh 268
320     If writing the session is successful call the session's
321     UnsetModified method
322     """
323     if saver_class is None:
324 jonathan 697 saver_class = SessionSaver
325 bh 268 saver = saver_class(session)
326     saver.write(file)
327    
328 bh 6 # 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