/[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 894 - (show annotations)
Mon May 12 10:46:29 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: 11911 byte(s)
Old comment deleted.

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 else:
155 return None
156
157 class SessionSaver(XMLWriter):
158
159 """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 XMLWriter.__init__(self)
172 self.session = session
173
174 def write(self, file_or_filename):
175 XMLWriter.write(self, file_or_filename)
176
177 self.write_header("session", "thuban.dtd")
178 self.write_session(self.session)
179 self.close()
180
181 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 self.open_element("session", attrs)
203 for map in session.Maps():
204 self.write_map(map)
205 self.close_element("session")
206
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 self.open_element('map title="%s"' % self.encode(map.title))
216 self.write_projection(map.projection)
217 for layer in map.Layers():
218 self.write_layer(layer)
219 self.write_label_layer(map.LabelLayer())
220 self.close_element('map')
221
222 def write_projection(self, projection):
223 """Write the projection.
224 """
225 if projection and len(projection.params) > 0:
226 self.open_element("projection", {"name": projection.GetName()})
227 for param in projection.params:
228 self.write_element('parameter value="%s"' %
229 self.encode(param))
230 self.close_element("projection")
231
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 lc = layer.GetClassification()
240
241 if attrs is None:
242 attrs = {}
243
244 attrs["title"] = layer.title
245 attrs["filename"] = relative_filename(self.dir, layer.filename)
246 attrs["stroke"] = lc.GetDefaultLineColor().hex()
247 attrs["stroke_width"] = str(lc.GetDefaultLineWidth())
248 attrs["fill"] = lc.GetDefaultFill().hex()
249 attrs["visible"] = ("false", "true")[int(layer.Visible())]
250
251 self.open_element("layer", attrs)
252
253 proj = layer.GetProjection()
254 if proj is not None:
255 self.write_projection(proj)
256
257 self.write_classification(layer)
258
259 self.close_element("layer")
260
261 def write_classification(self, layer, attrs = None):
262 if attrs is None:
263 attrs = {}
264
265 lc = layer.GetClassification()
266
267 field = lc.GetField()
268
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 attrs["field_type"] = str(lc.GetFieldType())
277 self.open_element("classification", attrs)
278
279
280 types = [[lambda p: 'clnull label="%s"' % self.encode(p.GetLabel()),
281 lambda p: 'clnull'],
282 [lambda p: 'clpoint label="%s" value="%s"' %
283 (self.encode(p.GetLabel()), str(p.GetValue())),
284 lambda p: 'clpoint'],
285 [lambda p: 'clrange label="%s" range="%s"' %
286 (self.encode(p.GetLabel()),
287 str(p.GetRange())),
288 lambda p: 'clrange']]
289
290 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 assert type >= 0
297
298 if type <= 2:
299 data = group.GetProperties()
300 dict = {'stroke' : data.GetLineColor().hex(),
301 'stroke_width': str(data.GetLineWidth()),
302 '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 for i in lc:
310 write_class_group(i)
311
312 self.close_element("classification")
313
314 def write_label_layer(self, layer):
315 """Write the label layer.
316 """
317 labels = layer.Labels()
318 if labels:
319 self.open_element('labellayer')
320 for label in labels:
321 self.write_element(('label x="%g" y="%g" text="%s"'
322 ' halign="%s" valign="%s"')
323 % (label.x, label.y,
324 self.encode(label.text),
325 label.halign,
326 label.valign))
327 self.close_element('labellayer')
328
329
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 SessionSaver.
339
340 If writing the session is successful call the session's
341 UnsetModified method
342 """
343 if saver_class is None:
344 saver_class = SessionSaver
345 saver = saver_class(session)
346 saver.write(file)
347
348 # 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