/[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 892 - (show annotations)
Mon May 12 10:45:47 2003 UTC (21 years, 9 months ago) by frank
Original Path: trunk/thuban/Thuban/Model/save.py
File MIME type: text/x-python
File size: 11958 byte(s)
(XMLWriter.encode()): Explicite call to unicode and latin1. Fixes #1851 finally.

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