/[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 920 - (show annotations)
Fri May 16 17:37:29 2003 UTC (21 years, 9 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/save.py
File MIME type: text/x-python
File size: 11743 byte(s)
Remove some unused imports including the
__future__ import for nested_scopes as Thuban relies on Python 2.2
now.
(XMLWriter.encode): Remove the special case for a None argument.
In the saver encode is always called with a string argument.

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 import string
18
19 import Thuban.Lib.fileutil
20
21 from Thuban.Model.classification import \
22 ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap
23
24 #
25 # one level of indention
26 #
27 TAB = " "
28
29 def relative_filename(dir, filename):
30 """Return a filename relative to dir for the absolute file name absname.
31
32 This is almost the same as the function in fileutil, except that dir
33 can be an empty string in which case filename will be returned
34 unchanged.
35 """
36 if dir:
37 return Thuban.Lib.fileutil.relative_filename(dir, filename)
38 else:
39 return filename
40
41 def escape(data):
42 """Escape &, \", ', <, and > in a string of data.
43 """
44 data = string.replace(data, "&", "&amp;")
45 data = string.replace(data, "<", "&lt;")
46 data = string.replace(data, ">", "&gt;")
47 data = string.replace(data, '"', "&quot;")
48 data = string.replace(data, "'", "&apos;")
49 return data
50
51 class XMLWriter:
52 """Abstract XMLWriter.
53
54 Should be overridden to provide specific object saving functionality.
55 """
56
57 def __init__(self):
58 self.filename = None
59 pass
60
61 def write(self, file_or_filename):
62 """Write the session to a file.
63
64 The argument may be either a file object or a filename. If it's
65 a filename, the file will be opened for writing. Files of
66 shapefiles will be stored as filenames relative to the directory
67 the file is stored in (as given by os.path.dirname(filename)) if
68 they have a common parent directory other than the root
69 directory.
70
71 If the argument is a file object (which is determined by the
72 presence of a write method) all filenames will be absolute
73 filenames.
74 """
75
76 # keep track of how many levels of indentation to write
77 self.indent_level = 0
78 # track whether an element is currently open. see open_element().
79 self.element_open = 0
80
81 if hasattr(file_or_filename, "write"):
82 # it's a file object
83 self.file = file_or_filename
84 self.dir = ""
85 else:
86 self.filename = file_or_filename
87 self.dir = os.path.dirname(self.filename)
88 self.file = open(self.filename, 'w')
89
90 def close(self):
91 assert self.indent_level == 0
92 if self.filename is not None:
93 self.file.close()
94
95 def write_header(self, doctype, system):
96 """Write the XML header"""
97 self.file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
98 self.file.write('<!DOCTYPE %s SYSTEM "%s">\n' % (doctype, system))
99
100 def open_element(self, element, attrs = {}):
101
102 #
103 # we note when an element is opened so that if two open_element()
104 # calls are made successively we can end the currently open
105 # tag and will later write a proper close tag. otherwise,
106 # if a close_element() call is made directly after an open_element()
107 # call we will close the tag with a />
108 #
109 if self.element_open == 1:
110 self.file.write(">\n")
111
112 self.element_open = 1
113
114 # Helper function to write an element open tag with attributes
115 self.file.write("%s<%s" % (TAB*self.indent_level, element))
116 self.__write_attribs(attrs)
117
118 self.indent_level += 1
119
120 def close_element(self, element):
121 self.indent_level -= 1
122 assert self.indent_level >= 0
123
124 # see open_element() for an explanation
125 if self.element_open == 1:
126 self.element_open = 0
127 self.file.write("/>\n")
128 else:
129 self.file.write("%s</%s>\n" % (TAB*self.indent_level, element))
130
131 def write_element(self, element, attrs = {}):
132 """write an element that won't need a closing tag"""
133 self.open_element(element, attrs)
134 self.close_element(element)
135
136 def __write_attribs(self, attrs):
137 for name, value in attrs.items():
138 self.file.write(' %s="%s"' % (self.encode(name),
139 self.encode(value)))
140
141 def encode(self, str):
142 """Return an XML-escaped and UTF-8 encoded copy of the string str.
143
144 Assume that the argument is a bytestring in Latin 1.
145 """
146 return unicode(escape(str),'latin1').encode("utf8")
147
148
149 class SessionSaver(XMLWriter):
150
151 """Class to serialize a session into an XML file.
152
153 Applications built on top of Thuban may derive from this class and
154 override or extend the methods to save additional information. This
155 additional information should take the form of additional attributes
156 or elements whose names are prefixed with a namespace. To define a
157 namespace derived classes should extend the write_session method to
158 pass the namespaces to the default implementation.
159 """
160
161
162 def __init__(self, session):
163 XMLWriter.__init__(self)
164 self.session = session
165
166 def write(self, file_or_filename):
167 XMLWriter.write(self, file_or_filename)
168
169 self.write_header("session", "thuban.dtd")
170 self.write_session(self.session)
171 self.close()
172
173 def write_session(self, session, attrs = None, namespaces = ()):
174 """Write the session and its contents
175
176 By default, write a session element with the title attribute and
177 call write_map for each map contained in the session.
178
179 The optional argument attrs is for additional attributes and, if
180 given, should be a mapping from attribute names to attribute
181 values. The values should not be XML-escaped yet.
182
183 The optional argument namespaces, if given, should be a sequence
184 of (name, URI) pairs. The namespaces are written as namespace
185 attributes into the session element. This is mainly useful for
186 derived classes that need to store additional information in a
187 thuban session file.
188 """
189 if attrs is None:
190 attrs = {}
191 attrs["title"] = session.title
192 for name, uri in namespaces:
193 attrs["xmlns:" + name] = uri
194 self.open_element("session", attrs)
195 for map in session.Maps():
196 self.write_map(map)
197 self.close_element("session")
198
199 def write_map(self, map):
200 """Write the map and its contents.
201
202 By default, write a map element element with the title
203 attribute, call write_projection to write the projection
204 element, call write_layer for each layer contained in the map
205 and finally call write_label_layer to write the label layer.
206 """
207 self.open_element('map title="%s"' % self.encode(map.title))
208 self.write_projection(map.projection)
209 for layer in map.Layers():
210 self.write_layer(layer)
211 self.write_label_layer(map.LabelLayer())
212 self.close_element('map')
213
214 def write_projection(self, projection):
215 """Write the projection.
216 """
217 if projection and len(projection.params) > 0:
218 self.open_element("projection", {"name": projection.GetName()})
219 for param in projection.params:
220 self.write_element('parameter value="%s"' %
221 self.encode(param))
222 self.close_element("projection")
223
224 def write_layer(self, layer, attrs = None):
225 """Write the layer.
226
227 The optional argument attrs is for additional attributes and, if
228 given, should be a mapping from attribute names to attribute
229 values. The values should not be XML-escaped yet.
230 """
231 lc = layer.GetClassification()
232
233 if attrs is None:
234 attrs = {}
235
236 attrs["title"] = layer.title
237 attrs["filename"] = relative_filename(self.dir, layer.filename)
238 attrs["stroke"] = lc.GetDefaultLineColor().hex()
239 attrs["stroke_width"] = str(lc.GetDefaultLineWidth())
240 attrs["fill"] = lc.GetDefaultFill().hex()
241 attrs["visible"] = ("false", "true")[int(layer.Visible())]
242
243 self.open_element("layer", attrs)
244
245 proj = layer.GetProjection()
246 if proj is not None:
247 self.write_projection(proj)
248
249 self.write_classification(layer)
250
251 self.close_element("layer")
252
253 def write_classification(self, layer, attrs = None):
254 if attrs is None:
255 attrs = {}
256
257 lc = layer.GetClassification()
258
259 field = lc.GetField()
260
261 #
262 # there isn't a classification of anything
263 # so don't do anything
264 #
265 if field is None: return
266
267 attrs["field"] = field
268 attrs["field_type"] = str(lc.GetFieldType())
269 self.open_element("classification", attrs)
270
271
272 types = [[lambda p: 'clnull label="%s"' % self.encode(p.GetLabel()),
273 lambda p: 'clnull'],
274 [lambda p: 'clpoint label="%s" value="%s"' %
275 (self.encode(p.GetLabel()), str(p.GetValue())),
276 lambda p: 'clpoint'],
277 [lambda p: 'clrange label="%s" range="%s"' %
278 (self.encode(p.GetLabel()),
279 str(p.GetRange())),
280 lambda p: 'clrange']]
281
282 def write_class_group(group):
283 type = -1
284 if isinstance(group, ClassGroupDefault): type = 0
285 elif isinstance(group, ClassGroupSingleton): type = 1
286 elif isinstance(group, ClassGroupRange): type = 2
287 elif isinstance(group, ClassGroupMap): type = 3
288 assert type >= 0
289
290 if type <= 2:
291 data = group.GetProperties()
292 dict = {'stroke' : data.GetLineColor().hex(),
293 'stroke_width': str(data.GetLineWidth()),
294 'fill' : data.GetFill().hex()}
295
296 self.open_element(types[type][0](group))
297 self.write_element("cldata", dict)
298 self.close_element(types[type][1](group))
299 else: pass # XXX: we need to handle maps
300
301 for i in lc:
302 write_class_group(i)
303
304 self.close_element("classification")
305
306 def write_label_layer(self, layer):
307 """Write the label layer.
308 """
309 labels = layer.Labels()
310 if labels:
311 self.open_element('labellayer')
312 for label in labels:
313 self.write_element(('label x="%g" y="%g" text="%s"'
314 ' halign="%s" valign="%s"')
315 % (label.x, label.y,
316 self.encode(label.text),
317 label.halign,
318 label.valign))
319 self.close_element('labellayer')
320
321
322
323 def save_session(session, file, saver_class = None):
324 """Save the session session to a file.
325
326 The file argument may either be a filename or an open file object.
327
328 The optional argument saver_class is the class to use to serialize
329 the session. By default or if it's None, the saver class will be
330 SessionSaver.
331
332 If writing the session is successful call the session's
333 UnsetModified method
334 """
335 if saver_class is None:
336 saver_class = SessionSaver
337 saver = saver_class(session)
338 saver.write(file)
339
340 # after a successful save consider the session unmodified.
341 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