/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/save.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/Model/save.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 74 by bh, Mon Feb 4 19:22:08 2002 UTC revision 414 by jonathan, Wed Feb 19 16:52:23 2003 UTC
# Line 2  Line 2 
2  # Authors:  # Authors:
3  # Jan-Oliver Wagner <[email protected]>  # Jan-Oliver Wagner <[email protected]>
4  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
5    # Jonathan Coles <[email protected]>
6  #  #
7  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
8  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
# Line 15  __version__ = "$Revision$" Line 16  __version__ = "$Revision$"
16  import os  import os
17  import string  import string
18    
19  from Thuban.Lib.fileutil import relative_filename  import Thuban.Lib.fileutil
20    
21    from Thuban.Model.color import Color
22    
23    #
24    # one level of indention
25    #
26    TAB = "    "
27    
28    def relative_filename(dir, filename):
29        """Return a filename relative to dir for the absolute file name absname.
30    
31        This is almost the same as the function in fileutil, except that dir
32        can be an empty string in which case filename will be returned
33        unchanged.
34        """
35        if dir:
36            return Thuban.Lib.fileutil.relative_filename(dir, filename)
37        else:
38            return filename
39    
40  def escape(data):  def escape(data):
41      """Escape &, \", ', <, and > in a string of data.      """Escape &, \", ', <, and > in a string of data.
# Line 27  def escape(data): Line 47  def escape(data):
47      data = string.replace(data, "'", "&apos;")      data = string.replace(data, "'", "&apos;")
48      return data      return data
49    
50  def save_session(session, filename):  class Saver:
     """Save the session session to the file given by filename"""  
     dir = os.path.dirname(filename)  
     file = open(filename, 'w')  
     write = file.write  
     write('<?xml version="1.0" encoding="UTF-8"?>\n')  
     write('<!DOCTYPE session SYSTEM "thuban.dtd">\n')  
     write('<session title="%s">\n' % escape(session.title))  
     for map in session.Maps():  
         write('\t<map title="%s">\n' % escape(map.title))  
         if map.projection and len(map.projection.params) > 0:  
             write('\t\t<projection>\n')  
             for param in map.projection.params:  
                 write('\t\t\t<parameter value="%s"/>\n' % escape(param))  
             write('\t\t</projection>\n')  
51    
52          for layer in map.Layers():      """Class to serialize a session into an XML file.
53              fill = layer.fill  
54              if fill is None:      Applications built on top of Thuban may derive from this class and
55                  fill = "None"      override or extend the methods to save additinal information. This
56              else:      additional information should take the form of additional attributes
57                  fill = fill.hex()      or elements whose names are prefixed with a namespace. To define a
58              stroke = layer.stroke      namespace derived classes should extend the write_session method to
59              if stroke is None:      pass the namespaces to the default implementation.
60                  stroke = "None"      """
61    
62    
63        def __init__(self, session):
64            self.session = session
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 absolut
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                filename = file_or_filename
92                self.dir = os.path.dirname(filename)
93                self.file = open(filename, 'w')
94            self.write_header()
95            self.write_session(self.session)
96    
97            if self.indent_level != 0:
98                raise ValueError("indent_level still positive!")
99    
100        def write_attribs(self, attrs):
101            for name, value in attrs.items():
102                if isinstance(value, Color):
103                    value = value.hex()
104              else:              else:
105                  stroke = stroke.hex()                  value = str(value)
106              write(('\t\t<layer title="%s" filename="%s"'              self.file.write(' %s="%s"' % (escape(name), escape(value)))
107                     ' fill="%s" stroke="%s" stroke_width="%d"/>\n') %      
108                    (escape(layer.title),      def open_element(self, element, attrs = {}):
109                     escape(relative_filename(dir, layer.filename)),  
110                     fill, stroke, layer.stroke_width))          #
111          labels = map.LabelLayer().Labels()          # we note when an element is opened so that if two open_element()
112            # calls are made successively we can end the currently open
113            # tag and will later write a proper close tag. otherwise,
114            # if a close_element() call is made directly after an open_element()
115            # call we will close the tag with a />
116            #
117            if self.element_open == 1:
118                self.file.write(">\n")
119    
120            self.element_open = 1
121    
122            # Helper function to write an element open tag with attributes
123            self.file.write("%s<%s" % (TAB*self.indent_level, element))
124            self.write_attribs(attrs)
125    
126            self.indent_level += 1
127    
128        def close_element(self, element):
129            self.indent_level -= 1
130            if self.indent_level < 0:
131                raise ValueError("close_element called too many times!")
132    
133            # see open_element() for an explanation
134            if self.element_open == 1:
135                self.element_open = 0
136                self.file.write("/>\n")
137            else:
138                self.file.write("%s</%s>\n" % (TAB*self.indent_level, element))
139    
140        def write_element(self, element, attrs = {}):
141            """write an element that won't need a closing tag"""
142            self.open_element(element, attrs)
143            self.close_element(element)
144    
145        def write_header(self):
146            """Write the XML header"""
147            self.file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
148            self.file.write('<!DOCTYPE session SYSTEM "thuban.dtd">\n')
149    
150        def write_session(self, session, attrs = None, namespaces = ()):
151            """Write the session and its contents
152    
153            By default, write a session element with the title attribute and
154            call write_map for each map contained in the session.
155    
156            The optional argument attrs is for additional attributes and, if
157            given, should be a mapping from attribute names to attribute
158            values. The values should not be XML-escaped yet.
159    
160            The optional argument namespaces, if given, should be a sequence
161            of (name, URI) pairs. The namespaces are written as namespace
162            attributes into the session element. This is mainly useful for
163            derived classes that need to store additional information in a
164            thuban session file.
165            """
166            if attrs is None:
167                attrs = {}
168            attrs["title"] = session.title
169            for name, uri in namespaces:
170                attrs["xmlns:" + name] = uri
171            self.open_element("session", attrs)
172            for map in session.Maps():
173                self.write_map(map)
174            self.close_element("session")
175    
176        def write_map(self, map):
177            """Write the map and its contents.
178    
179            By default, write a map element element with the title
180            attribute, call write_projection to write the projection
181            element, call write_layer for each layer contained in the map
182            and finally call write_label_layer to write the label layer.
183            """
184            write = self.file.write
185            self.open_element('map title="%s"' % escape(map.title))
186            self.write_projection(map.projection)
187            for layer in map.Layers():
188                self.write_layer(layer)
189            self.write_label_layer(map.LabelLayer())
190            self.close_element('map')
191    
192        def write_projection(self, projection):
193            """Write the projection.
194            """
195            if projection and len(projection.params) > 0:
196                self.open_element("projection")
197                for param in projection.params:
198                    self.write_element('parameter value="%s"' % escape(param))
199                self.close_element("projection")
200    
201        def write_layer(self, layer, attrs = None):
202            """Write the layer.
203    
204            The optional argument attrs is for additional attributes and, if
205            given, should be a mapping from attribute names to attribute
206            values. The values should not be XML-escaped yet.
207            """
208            lc = layer.GetClassification()
209    
210            if attrs is None:
211                attrs = {}
212            attrs["title"] = layer.title
213            attrs["filename"] = relative_filename(self.dir, layer.filename)
214            attrs["stroke"] = lc.GetDefaultStroke().hex()
215            attrs["stroke_width"] = str(lc.GetDefaultStrokeWidth())
216            attrs["fill"] = lc.GetDefaultFill().hex()
217    
218            #fill = lc.GetDefaultFill()
219            #if fill is None:
220                #attrs["fill"] = "None"
221            #else:
222                #attrs["fill"] = fill.hex()
223    
224    
225            #stroke = lc.GetDefaultStroke()
226            #if stroke is None:
227                #attrs["stroke"] = "None"
228            #else:
229                #attrs["stroke"] = stroke.hex()
230    
231            self.open_element("layer", attrs)
232            self.write_classification(layer)
233            self.close_element("layer")
234    
235        def write_classification(self, layer, attrs = None):
236            if attrs is None:
237                attrs = {}
238    
239            lc = layer.GetClassification()
240    
241            field = lc.field
242    
243            #
244            # there isn't a classification of anything
245            # so don't do anything
246            #
247            if field is None: return
248    
249            attrs["field"] = field
250            self.open_element("classification", attrs)
251    
252            def write_class_data(data):
253                dict = {'stroke'      : data.GetStroke().hex(),
254                        'stroke_width': str(data.GetStrokeWidth()),
255                        'fill'        : data.GetFill().hex()}
256                self.write_element("cldata", dict)
257    
258            self.open_element("clnull")
259            write_class_data(lc.GetDefaultData())
260            self.close_element("clnull")
261                
262            if lc.points != {}:
263                for p in lc.points.values():
264                    self.open_element('clpoint value="%s"' %
265                        (escape(str(p.GetValue()))))
266                    write_class_data(p)
267                    self.close_element('clpoint')
268              
269            if lc.ranges != []:
270                for p in lc.ranges:
271                    self.open_element('clrange min="%s" max="%s"'
272                        % (escape(str(p.GetMin())), escape(str(p.GetMax()))))
273                    write_class_data(p)
274                    self.close_element('clrange')
275    
276            self.close_element("classification")
277    
278        def write_label_layer(self, layer):
279            """Write the label layer.
280            """
281            labels = layer.Labels()
282          if labels:          if labels:
283              write('\t\t<labellayer>\n')              self.open_element('labellayer')
284              for label in labels:              for label in labels:
285                  write(('\t\t\t<label x="%g" y="%g" text="%s"'                  self.write_element(('label x="%g" y="%g" text="%s"'
286                         ' halign="%s" valign="%s"/>\n')                                      ' halign="%s" valign="%s"')
287                        % (label.x, label.y, label.text, label.halign,                                  % (label.x, label.y, label.text, label.halign,
288                           label.valign))                                     label.valign))
289              write('\t\t</labellayer>\n')              self.close_element('labellayer')
290          write('\t</map>\n')  
291      write('</session>\n')  
292    
293    def save_session(session, file, saver_class = None):
294        """Save the session session to a file.
295    
296        The file argument may either be a filename or an open file object.
297    
298        The optional argument saver_class is the class to use to serialize
299        the session. By default or if it's None, the saver class will be
300        Saver.
301    
302        If writing the session is successful call the session's
303        UnsetModified method
304        """
305        if saver_class is None:
306            saver_class = Saver
307        saver = saver_class(session)
308        saver.write(file)
309    
310      # after a successful save consider the session unmodified.      # after a successful save consider the session unmodified.
311      session.UnsetModified()      session.UnsetModified()

Legend:
Removed from v.74  
changed lines
  Added in v.414

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26