/[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 876 - (hide annotations)
Fri May 9 16:31:55 2003 UTC (21 years, 10 months ago) by jonathan
Original Path: trunk/thuban/Thuban/Model/save.py
File MIME type: text/x-python
File size: 11893 byte(s)
Explicit imports.
(XMLWriter.encode): New. Encode the given string from a format
        used by Thuban into UTF-8. Fixes RTbug #1851.

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     return escape(str).encode("utf8")
154     else:
155     return None
156    
157 jonathan 710 class SessionSaver(XMLWriter):
158 bh 268
159 jonathan 697 """Class to serialize a session into an XML file.
160    
161     Applications built on top of Thuban may derive from this class and
162     override or extend the methods to save additional information. This
163     additional information should take the form of additional attributes
164     or elements whose names are prefixed with a namespace. To define a
165     namespace derived classes should extend the write_session method to
166     pass the namespaces to the default implementation.
167     """
168    
169    
170     def __init__(self, session):
171 jonathan 710 XMLWriter.__init__(self)
172 jonathan 697 self.session = session
173    
174     def write(self, file_or_filename):
175 jonathan 710 XMLWriter.write(self, file_or_filename)
176 jonathan 697
177     self.write_header("session", "thuban.dtd")
178     self.write_session(self.session)
179     self.close()
180    
181 bh 268 def write_session(self, session, attrs = None, namespaces = ()):
182     """Write the session and its contents
183    
184     By default, write a session element with the title attribute and
185     call write_map for each map contained in the session.
186    
187     The optional argument attrs is for additional attributes and, if
188     given, should be a mapping from attribute names to attribute
189     values. The values should not be XML-escaped yet.
190    
191     The optional argument namespaces, if given, should be a sequence
192     of (name, URI) pairs. The namespaces are written as namespace
193     attributes into the session element. This is mainly useful for
194     derived classes that need to store additional information in a
195     thuban session file.
196     """
197     if attrs is None:
198     attrs = {}
199     attrs["title"] = session.title
200     for name, uri in namespaces:
201     attrs["xmlns:" + name] = uri
202 jonathan 366 self.open_element("session", attrs)
203 bh 268 for map in session.Maps():
204     self.write_map(map)
205 jonathan 366 self.close_element("session")
206 bh 268
207     def write_map(self, map):
208     """Write the map and its contents.
209    
210     By default, write a map element element with the title
211     attribute, call write_projection to write the projection
212     element, call write_layer for each layer contained in the map
213     and finally call write_label_layer to write the label layer.
214     """
215 jonathan 876 self.open_element('map title="%s"' % self.encode(map.title))
216 bh 268 self.write_projection(map.projection)
217     for layer in map.Layers():
218     self.write_layer(layer)
219     self.write_label_layer(map.LabelLayer())
220 jonathan 366 self.close_element('map')
221 bh 6
222 bh 268 def write_projection(self, projection):
223     """Write the projection.
224     """
225     if projection and len(projection.params) > 0:
226 jonathan 876 self.open_element("projection", {"name": projection.GetName()})
227 bh 268 for param in projection.params:
228 jonathan 876 self.write_element('parameter value="%s"' %
229     self.encode(param))
230 jonathan 366 self.close_element("projection")
231 bh 268
232     def write_layer(self, layer, attrs = None):
233     """Write the layer.
234    
235     The optional argument attrs is for additional attributes and, if
236     given, should be a mapping from attribute names to attribute
237     values. The values should not be XML-escaped yet.
238     """
239 jonathan 414 lc = layer.GetClassification()
240 jonathan 391
241 bh 268 if attrs is None:
242     attrs = {}
243 jonathan 429
244     attrs["title"] = layer.title
245     attrs["filename"] = relative_filename(self.dir, layer.filename)
246 jonathan 466 attrs["stroke"] = lc.GetDefaultLineColor().hex()
247     attrs["stroke_width"] = str(lc.GetDefaultLineWidth())
248 jonathan 429 attrs["fill"] = lc.GetDefaultFill().hex()
249 jonathan 773 attrs["visible"] = ("false", "true")[int(layer.Visible())]
250 bh 268
251 jonathan 366 self.open_element("layer", attrs)
252 jonathan 740
253     proj = layer.GetProjection()
254     if proj is not None:
255     self.write_projection(proj)
256    
257 jonathan 366 self.write_classification(layer)
258 jonathan 740
259 jonathan 366 self.close_element("layer")
260    
261     def write_classification(self, layer, attrs = None):
262     if attrs is None:
263     attrs = {}
264    
265 jonathan 414 lc = layer.GetClassification()
266 jonathan 366
267 jonathan 429 field = lc.GetField()
268 jonathan 366
269     #
270     # there isn't a classification of anything
271     # so don't do anything
272     #
273     if field is None: return
274    
275     attrs["field"] = field
276 jonathan 466 attrs["field_type"] = str(lc.GetFieldType())
277 jonathan 366 self.open_element("classification", attrs)
278    
279 jonathan 429
280 jonathan 876 types = [[lambda p: 'clnull label="%s"' % self.encode(p.GetLabel()),
281 jonathan 440 lambda p: 'clnull'],
282 jonathan 683 [lambda p: 'clpoint label="%s" value="%s"' %
283 jonathan 876 (self.encode(p.GetLabel()), str(p.GetValue())),
284 jonathan 440 lambda p: 'clpoint'],
285 jonathan 876 [lambda p: 'clrange label="%s" range="%s"' %
286     (self.encode(p.GetLabel()),
287     str(p.GetRange())),
288 jonathan 440 lambda p: 'clrange']]
289 jonathan 429
290 jonathan 440 def write_class_group(group):
291     type = -1
292     if isinstance(group, ClassGroupDefault): type = 0
293     elif isinstance(group, ClassGroupSingleton): type = 1
294     elif isinstance(group, ClassGroupRange): type = 2
295     elif isinstance(group, ClassGroupMap): type = 3
296 jonathan 605 assert type >= 0
297 jonathan 366
298 jonathan 440 if type <= 2:
299     data = group.GetProperties()
300 jonathan 466 dict = {'stroke' : data.GetLineColor().hex(),
301     'stroke_width': str(data.GetLineWidth()),
302 jonathan 440 'fill' : data.GetFill().hex()}
303    
304     self.open_element(types[type][0](group))
305     self.write_element("cldata", dict)
306     self.close_element(types[type][1](group))
307     else: pass # XXX: we need to handle maps
308    
309 jonathan 429 for i in lc:
310 jonathan 440 write_class_group(i)
311 jonathan 429
312 jonathan 366 self.close_element("classification")
313    
314 bh 268 def write_label_layer(self, layer):
315     """Write the label layer.
316     """
317     labels = layer.Labels()
318 bh 6 if labels:
319 jonathan 366 self.open_element('labellayer')
320 bh 6 for label in labels:
321 jonathan 366 self.write_element(('label x="%g" y="%g" text="%s"'
322     ' halign="%s" valign="%s"')
323 jonathan 876 % (label.x, label.y,
324     self.encode(label.text),
325     label.halign,
326 bh 268 label.valign))
327 jonathan 366 self.close_element('labellayer')
328 bh 6
329 bh 268
330    
331     def save_session(session, file, saver_class = None):
332     """Save the session session to a file.
333    
334     The file argument may either be a filename or an open file object.
335    
336     The optional argument saver_class is the class to use to serialize
337     the session. By default or if it's None, the saver class will be
338 jonathan 697 SessionSaver.
339 bh 268
340     If writing the session is successful call the session's
341     UnsetModified method
342     """
343     if saver_class is None:
344 jonathan 697 saver_class = SessionSaver
345 bh 268 saver = saver_class(session)
346     saver.write(file)
347    
348 bh 6 # after a successful save consider the session unmodified.
349     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