/[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 892 - (hide 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 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 jonathan 440 # fix for people using python2.1
17     from __future__ import nested_scopes
18    
19 bh 6 import os
20     import string
21    
22 bh 201 import Thuban.Lib.fileutil
23 bh 6
24 jonathan 366 from Thuban.Model.color import Color
25    
26 jonathan 876 from Thuban.Model.classification import \
27     ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap
28 jonathan 429
29 jonathan 366 #
30     # one level of indention
31     #
32     TAB = " "
33    
34 bh 201 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 bh 6 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 jonathan 710 class XMLWriter:
57     """Abstract XMLWriter.
58 bh 268
59 jonathan 697 Should be overridden to provide specific object saving functionality.
60 bh 268 """
61    
62 jonathan 697 def __init__(self):
63     self.filename = None
64     pass
65 jonathan 366
66 bh 268 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 jonathan 697 presence of a write method) all filenames will be absolute
78 bh 268 filenames.
79     """
80 jonathan 366
81 jonathan 391 # 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 jonathan 366
86 bh 268 if hasattr(file_or_filename, "write"):
87     # it's a file object
88     self.file = file_or_filename
89     self.dir = ""
90     else:
91 jonathan 697 self.filename = file_or_filename
92     self.dir = os.path.dirname(self.filename)
93     self.file = open(self.filename, 'w')
94 bh 268
95 jonathan 697 def close(self):
96 jonathan 605 assert self.indent_level == 0
97 jonathan 697 if self.filename is not None:
98     self.file.close()
99 jonathan 366
100 jonathan 697 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 jonathan 366 def open_element(self, element, attrs = {}):
106 jonathan 391
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 jonathan 366 # Helper function to write an element open tag with attributes
120     self.file.write("%s<%s" % (TAB*self.indent_level, element))
121 jonathan 697 self.__write_attribs(attrs)
122 bh 268
123 jonathan 366 self.indent_level += 1
124    
125     def close_element(self, element):
126     self.indent_level -= 1
127 jonathan 605 assert self.indent_level >= 0
128 jonathan 366
129 jonathan 391 # 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 jonathan 366 def write_element(self, element, attrs = {}):
137 jonathan 391 """write an element that won't need a closing tag"""
138     self.open_element(element, attrs)
139     self.close_element(element)
140 jonathan 366
141 jonathan 697 def __write_attribs(self, attrs):
142     for name, value in attrs.items():
143 jonathan 876 self.file.write(' %s="%s"' % (self.encode(name),
144     self.encode(value)))
145 jonathan 697
146 jonathan 876 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 frank 892 return unicode(escape(str),'latin1').encode("utf8")
154     #return escape(str).encode("utf8")
155 jonathan 876 else:
156     return None
157    
158 jonathan 710 class SessionSaver(XMLWriter):
159 bh 268
160 jonathan 697 """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 jonathan 710 XMLWriter.__init__(self)
173 jonathan 697 self.session = session
174    
175     def write(self, file_or_filename):
176 jonathan 710 XMLWriter.write(self, file_or_filename)
177 jonathan 697
178     self.write_header("session", "thuban.dtd")
179     self.write_session(self.session)
180     self.close()
181    
182 bh 268 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 jonathan 366 self.open_element("session", attrs)
204 bh 268 for map in session.Maps():
205     self.write_map(map)
206 jonathan 366 self.close_element("session")
207 bh 268
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 jonathan 876 self.open_element('map title="%s"' % self.encode(map.title))
217 bh 268 self.write_projection(map.projection)
218     for layer in map.Layers():
219     self.write_layer(layer)
220     self.write_label_layer(map.LabelLayer())
221 jonathan 366 self.close_element('map')
222 bh 6
223 bh 268 def write_projection(self, projection):
224     """Write the projection.
225     """
226     if projection and len(projection.params) > 0:
227 jonathan 876 self.open_element("projection", {"name": projection.GetName()})
228 bh 268 for param in projection.params:
229 jonathan 876 self.write_element('parameter value="%s"' %
230     self.encode(param))
231 jonathan 366 self.close_element("projection")
232 bh 268
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 jonathan 414 lc = layer.GetClassification()
241 jonathan 391
242 bh 268 if attrs is None:
243     attrs = {}
244 jonathan 429
245     attrs["title"] = layer.title
246     attrs["filename"] = relative_filename(self.dir, layer.filename)
247 jonathan 466 attrs["stroke"] = lc.GetDefaultLineColor().hex()
248     attrs["stroke_width"] = str(lc.GetDefaultLineWidth())
249 jonathan 429 attrs["fill"] = lc.GetDefaultFill().hex()
250 jonathan 773 attrs["visible"] = ("false", "true")[int(layer.Visible())]
251 bh 268
252 jonathan 366 self.open_element("layer", attrs)
253 jonathan 740
254     proj = layer.GetProjection()
255     if proj is not None:
256     self.write_projection(proj)
257    
258 jonathan 366 self.write_classification(layer)
259 jonathan 740
260 jonathan 366 self.close_element("layer")
261    
262     def write_classification(self, layer, attrs = None):
263     if attrs is None:
264     attrs = {}
265    
266 jonathan 414 lc = layer.GetClassification()
267 jonathan 366
268 jonathan 429 field = lc.GetField()
269 jonathan 366
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 jonathan 466 attrs["field_type"] = str(lc.GetFieldType())
278 jonathan 366 self.open_element("classification", attrs)
279    
280 jonathan 429
281 jonathan 876 types = [[lambda p: 'clnull label="%s"' % self.encode(p.GetLabel()),
282 jonathan 440 lambda p: 'clnull'],
283 jonathan 683 [lambda p: 'clpoint label="%s" value="%s"' %
284 jonathan 876 (self.encode(p.GetLabel()), str(p.GetValue())),
285 jonathan 440 lambda p: 'clpoint'],
286 jonathan 876 [lambda p: 'clrange label="%s" range="%s"' %
287     (self.encode(p.GetLabel()),
288     str(p.GetRange())),
289 jonathan 440 lambda p: 'clrange']]
290 jonathan 429
291 jonathan 440 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 jonathan 605 assert type >= 0
298 jonathan 366
299 jonathan 440 if type <= 2:
300     data = group.GetProperties()
301 jonathan 466 dict = {'stroke' : data.GetLineColor().hex(),
302     'stroke_width': str(data.GetLineWidth()),
303 jonathan 440 '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 jonathan 429 for i in lc:
311 jonathan 440 write_class_group(i)
312 jonathan 429
313 jonathan 366 self.close_element("classification")
314    
315 bh 268 def write_label_layer(self, layer):
316     """Write the label layer.
317     """
318     labels = layer.Labels()
319 bh 6 if labels:
320 jonathan 366 self.open_element('labellayer')
321 bh 6 for label in labels:
322 jonathan 366 self.write_element(('label x="%g" y="%g" text="%s"'
323     ' halign="%s" valign="%s"')
324 jonathan 876 % (label.x, label.y,
325     self.encode(label.text),
326     label.halign,
327 bh 268 label.valign))
328 jonathan 366 self.close_element('labellayer')
329 bh 6
330 bh 268
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 jonathan 697 SessionSaver.
340 bh 268
341     If writing the session is successful call the session's
342     UnsetModified method
343     """
344     if saver_class is None:
345 jonathan 697 saver_class = SessionSaver
346 bh 268 saver = saver_class(session)
347     saver.write(file)
348    
349 bh 6 # 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