/[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 920 - (hide 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 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     import string
18    
19 bh 201 import Thuban.Lib.fileutil
20 bh 6
21 jonathan 876 from Thuban.Model.classification import \
22     ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap
23 jonathan 429
24 jonathan 366 #
25     # one level of indention
26     #
27     TAB = " "
28    
29 bh 201 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 bh 6 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 jonathan 710 class XMLWriter:
52     """Abstract XMLWriter.
53 bh 268
54 jonathan 697 Should be overridden to provide specific object saving functionality.
55 bh 268 """
56    
57 jonathan 697 def __init__(self):
58     self.filename = None
59     pass
60 jonathan 366
61 bh 268 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 jonathan 697 presence of a write method) all filenames will be absolute
73 bh 268 filenames.
74     """
75 jonathan 366
76 jonathan 391 # 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 jonathan 366
81 bh 268 if hasattr(file_or_filename, "write"):
82     # it's a file object
83     self.file = file_or_filename
84     self.dir = ""
85     else:
86 jonathan 697 self.filename = file_or_filename
87     self.dir = os.path.dirname(self.filename)
88     self.file = open(self.filename, 'w')
89 bh 268
90 jonathan 697 def close(self):
91 jonathan 605 assert self.indent_level == 0
92 jonathan 697 if self.filename is not None:
93     self.file.close()
94 jonathan 366
95 jonathan 697 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 jonathan 366 def open_element(self, element, attrs = {}):
101 jonathan 391
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 jonathan 366 # Helper function to write an element open tag with attributes
115     self.file.write("%s<%s" % (TAB*self.indent_level, element))
116 jonathan 697 self.__write_attribs(attrs)
117 bh 268
118 jonathan 366 self.indent_level += 1
119    
120     def close_element(self, element):
121     self.indent_level -= 1
122 jonathan 605 assert self.indent_level >= 0
123 jonathan 366
124 jonathan 391 # 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 jonathan 366 def write_element(self, element, attrs = {}):
132 jonathan 391 """write an element that won't need a closing tag"""
133     self.open_element(element, attrs)
134     self.close_element(element)
135 jonathan 366
136 jonathan 697 def __write_attribs(self, attrs):
137     for name, value in attrs.items():
138 jonathan 876 self.file.write(' %s="%s"' % (self.encode(name),
139     self.encode(value)))
140 bh 920
141 jonathan 876 def encode(self, str):
142 bh 920 """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 jonathan 876 """
146 bh 920 return unicode(escape(str),'latin1').encode("utf8")
147 jonathan 876
148    
149 jonathan 710 class SessionSaver(XMLWriter):
150 bh 268
151 jonathan 697 """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 jonathan 710 XMLWriter.__init__(self)
164 jonathan 697 self.session = session
165    
166     def write(self, file_or_filename):
167 jonathan 710 XMLWriter.write(self, file_or_filename)
168 jonathan 697
169     self.write_header("session", "thuban.dtd")
170     self.write_session(self.session)
171     self.close()
172    
173 bh 268 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 jonathan 366 self.open_element("session", attrs)
195 bh 268 for map in session.Maps():
196     self.write_map(map)
197 jonathan 366 self.close_element("session")
198 bh 268
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 jonathan 876 self.open_element('map title="%s"' % self.encode(map.title))
208 bh 268 self.write_projection(map.projection)
209     for layer in map.Layers():
210     self.write_layer(layer)
211     self.write_label_layer(map.LabelLayer())
212 jonathan 366 self.close_element('map')
213 bh 6
214 bh 268 def write_projection(self, projection):
215     """Write the projection.
216     """
217     if projection and len(projection.params) > 0:
218 jonathan 876 self.open_element("projection", {"name": projection.GetName()})
219 bh 268 for param in projection.params:
220 jonathan 876 self.write_element('parameter value="%s"' %
221     self.encode(param))
222 jonathan 366 self.close_element("projection")
223 bh 268
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 jonathan 414 lc = layer.GetClassification()
232 jonathan 391
233 bh 268 if attrs is None:
234     attrs = {}
235 jonathan 429
236     attrs["title"] = layer.title
237     attrs["filename"] = relative_filename(self.dir, layer.filename)
238 jonathan 466 attrs["stroke"] = lc.GetDefaultLineColor().hex()
239     attrs["stroke_width"] = str(lc.GetDefaultLineWidth())
240 jonathan 429 attrs["fill"] = lc.GetDefaultFill().hex()
241 jonathan 773 attrs["visible"] = ("false", "true")[int(layer.Visible())]
242 bh 268
243 jonathan 366 self.open_element("layer", attrs)
244 jonathan 740
245     proj = layer.GetProjection()
246     if proj is not None:
247     self.write_projection(proj)
248    
249 jonathan 366 self.write_classification(layer)
250 jonathan 740
251 jonathan 366 self.close_element("layer")
252    
253     def write_classification(self, layer, attrs = None):
254     if attrs is None:
255     attrs = {}
256    
257 jonathan 414 lc = layer.GetClassification()
258 jonathan 366
259 jonathan 429 field = lc.GetField()
260 jonathan 366
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 jonathan 466 attrs["field_type"] = str(lc.GetFieldType())
269 jonathan 366 self.open_element("classification", attrs)
270    
271 jonathan 429
272 jonathan 876 types = [[lambda p: 'clnull label="%s"' % self.encode(p.GetLabel()),
273 jonathan 440 lambda p: 'clnull'],
274 jonathan 683 [lambda p: 'clpoint label="%s" value="%s"' %
275 jonathan 876 (self.encode(p.GetLabel()), str(p.GetValue())),
276 jonathan 440 lambda p: 'clpoint'],
277 jonathan 876 [lambda p: 'clrange label="%s" range="%s"' %
278     (self.encode(p.GetLabel()),
279     str(p.GetRange())),
280 jonathan 440 lambda p: 'clrange']]
281 jonathan 429
282 jonathan 440 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 jonathan 605 assert type >= 0
289 jonathan 366
290 jonathan 440 if type <= 2:
291     data = group.GetProperties()
292 jonathan 466 dict = {'stroke' : data.GetLineColor().hex(),
293     'stroke_width': str(data.GetLineWidth()),
294 jonathan 440 '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 jonathan 429 for i in lc:
302 jonathan 440 write_class_group(i)
303 jonathan 429
304 jonathan 366 self.close_element("classification")
305    
306 bh 268 def write_label_layer(self, layer):
307     """Write the label layer.
308     """
309     labels = layer.Labels()
310 bh 6 if labels:
311 jonathan 366 self.open_element('labellayer')
312 bh 6 for label in labels:
313 jonathan 366 self.write_element(('label x="%g" y="%g" text="%s"'
314     ' halign="%s" valign="%s"')
315 jonathan 876 % (label.x, label.y,
316     self.encode(label.text),
317     label.halign,
318 bh 268 label.valign))
319 jonathan 366 self.close_element('labellayer')
320 bh 6
321 bh 268
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 jonathan 697 SessionSaver.
331 bh 268
332     If writing the session is successful call the session's
333     UnsetModified method
334     """
335     if saver_class is None:
336 jonathan 697 saver_class = SessionSaver
337 bh 268 saver = saver_class(session)
338     saver.write(file)
339    
340 bh 6 # 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