/[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 1417 - (hide annotations)
Tue Jul 15 08:43:53 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: 12819 byte(s)
* Thuban/Model/save.py (SessionSaver.write_classification): Encode
string values (in addition to the labels) as UTF 8

* Thuban/Model/load.py (SessionLoader.start_clpoint): Decode the
values if the field type is string

* test/test_save.py (SaveSessionTest.testClassifiedLayer): Test
saving a session with non-ascii string classification values.

* test/test_load.py (TestClassification.file_contents)
(TestClassification.test): Check for non-ascii values in string
classifications

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 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 1375 self.write_header("session", "thuban-0.9.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 bh 1375 "http://thuban.intevation.org/dtds/thuban-0.9-dev.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 bh 1375 "leftcolumn": left_field,
187     "jointype": container.JoinType()})
188 bh 1268 else:
189     raise ValueError("Can't handle container %r" % container)
190    
191    
192 bh 268 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 jonathan 876 self.open_element('map title="%s"' % self.encode(map.title))
201 bh 268 self.write_projection(map.projection)
202     for layer in map.Layers():
203     self.write_layer(layer)
204     self.write_label_layer(map.LabelLayer())
205 jonathan 366 self.close_element('map')
206 bh 6
207 bh 268 def write_projection(self, projection):
208     """Write the projection.
209     """
210     if projection and len(projection.params) > 0:
211 jonathan 876 self.open_element("projection", {"name": projection.GetName()})
212 bh 268 for param in projection.params:
213 jonathan 876 self.write_element('parameter value="%s"' %
214     self.encode(param))
215 jonathan 366 self.close_element("projection")
216 bh 268
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 jonathan 391
225 bh 268 if attrs is None:
226     attrs = {}
227 jonathan 429
228 jonathan 1251 attrs["title"] = layer.title
229     attrs["visible"] = ("false", "true")[int(layer.Visible())]
230 bh 268
231 jonathan 932 if isinstance(layer, Layer):
232 bh 1268 attrs["shapestore"] = self.get_id(layer.ShapeStore())
233 jonathan 740
234 jonathan 932 lc = layer.GetClassification()
235 bh 1268 attrs["stroke"] = lc.GetDefaultLineColor().hex()
236 jonathan 932 attrs["stroke_width"] = str(lc.GetDefaultLineWidth())
237 bh 1268 attrs["fill"] = lc.GetDefaultFill().hex()
238 jonathan 740
239 jonathan 932 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 bh 1219 attrs["filename"] = relative_filename(self.dir, layer.filename)
245 jonathan 932 self.open_element("rasterlayer", attrs)
246     self.write_projection(layer.GetProjection())
247     self.close_element("rasterlayer")
248    
249 jonathan 366 def write_classification(self, layer, attrs = None):
250 jonathan 1251 """Write Classification information."""
251    
252 jonathan 366 if attrs is None:
253     attrs = {}
254    
255 jonathan 414 lc = layer.GetClassification()
256 jonathan 366
257 jonathan 429 field = lc.GetField()
258 jonathan 366
259     #
260 jonathan 1251 # there isn't a classification of anything so do nothing
261 jonathan 366 #
262     if field is None: return
263    
264     attrs["field"] = field
265 jonathan 466 attrs["field_type"] = str(lc.GetFieldType())
266 jonathan 366 self.open_element("classification", attrs)
267    
268 jonathan 1251 for g in lc:
269     if isinstance(g, ClassGroupDefault):
270     open_el = 'clnull label="%s"' % self.encode(g.GetLabel())
271     close_el = 'clnull'
272 bh 1417 elif isinstance(g, ClassGroupSingleton):
273     if lc.GetFieldType() == FIELDTYPE_STRING:
274     value = self.encode(g.GetValue())
275     else:
276     value = str(g.GetValue())
277 jonathan 1251 open_el = 'clpoint label="%s" value="%s"' \
278 bh 1417 % (self.encode(g.GetLabel()), value)
279 jonathan 1251 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 jonathan 429
288 jonathan 1251 data = g.GetProperties()
289     dict = {'stroke' : data.GetLineColor().hex(),
290     'stroke_width': str(data.GetLineWidth()),
291     'fill' : data.GetFill().hex()}
292 jonathan 429
293 jonathan 1251 self.open_element(open_el)
294     self.write_element("cldata", dict)
295     self.close_element(close_el)
296 jonathan 366
297     self.close_element("classification")
298    
299 bh 268 def write_label_layer(self, layer):
300     """Write the label layer.
301     """
302     labels = layer.Labels()
303 bh 6 if labels:
304 jonathan 366 self.open_element('labellayer')
305 bh 6 for label in labels:
306 jonathan 366 self.write_element(('label x="%g" y="%g" text="%s"'
307     ' halign="%s" valign="%s"')
308 jonathan 876 % (label.x, label.y,
309     self.encode(label.text),
310     label.halign,
311 bh 268 label.valign))
312 jonathan 366 self.close_element('labellayer')
313 bh 6
314 bh 268
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 jonathan 697 SessionSaver.
324 bh 268
325     If writing the session is successful call the session's
326     UnsetModified method
327     """
328     if saver_class is None:
329 jonathan 697 saver_class = SessionSaver
330 bh 268 saver = saver_class(session)
331     saver.write(file)
332    
333 bh 6 # 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