/[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 740 - (show annotations)
Fri Apr 25 09:14:04 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: 11241 byte(s)
(SessionSaver.write_layer): Save projection information.

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