/[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 440 by jonathan, Thu Feb 27 15:54:27 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 12  Functions to save a session to a file Line 13  Functions to save a session to a file
13    
14  __version__ = "$Revision$"  __version__ = "$Revision$"
15    
16    # fix for people using python2.1
17    from __future__ import nested_scopes
18    
19  import os  import os
20  import string  import string
21    
22  from Thuban.Lib.fileutil import relative_filename  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):  def escape(data):
46      """Escape &, \", ', <, and > in a string of data.      """Escape &, \", ', <, and > in a string of data.
# Line 27  def escape(data): Line 52  def escape(data):
52      data = string.replace(data, "'", "&apos;")      data = string.replace(data, "'", "&apos;")
53      return data      return data
54    
55  def save_session(session, filename):  class Saver:
56      """Save the session session to the file given by filename"""  
57      dir = os.path.dirname(filename)      """Class to serialize a session into an XML file.
58      file = open(filename, 'w')  
59      write = file.write      Applications built on top of Thuban may derive from this class and
60      write('<?xml version="1.0" encoding="UTF-8"?>\n')      override or extend the methods to save additinal information. This
61      write('<!DOCTYPE session SYSTEM "thuban.dtd">\n')      additional information should take the form of additional attributes
62      write('<session title="%s">\n' % escape(session.title))      or elements whose names are prefixed with a namespace. To define a
63      for map in session.Maps():      namespace derived classes should extend the write_session method to
64          write('\t<map title="%s">\n' % escape(map.title))      pass the namespaces to the default implementation.
65          if map.projection and len(map.projection.params) > 0:      """
66              write('\t\t<projection>\n')  
67              for param in map.projection.params:  
68                  write('\t\t\t<parameter value="%s"/>\n' % escape(param))      def __init__(self, session):
69              write('\t\t</projection>\n')          self.session = session
70    
71        def write(self, file_or_filename):
72            """Write the session to a file.
73    
74            The argument may be either a file object or a filename. If it's
75            a filename, the file will be opened for writing. Files of
76            shapefiles will be stored as filenames relative to the directory
77            the file is stored in (as given by os.path.dirname(filename)) if
78            they have a common parent directory other than the root
79            directory.
80    
81            If the argument is a file object (which is determined by the
82            presence of a write method) all filenames will be absolut
83            filenames.
84            """
85    
86            # keep track of how many levels of indentation to write
87            self.indent_level = 0
88            # track whether an element is currently open. see open_element().
89            self.element_open = 0
90    
91            if hasattr(file_or_filename, "write"):
92                # it's a file object
93                self.file = file_or_filename
94                self.dir = ""
95            else:
96                filename = file_or_filename
97                self.dir = os.path.dirname(filename)
98                self.file = open(filename, 'w')
99            self.write_header()
100            self.write_session(self.session)
101    
102            assert(self.indent_level == 0)
103    
104        def write_attribs(self, attrs):
105            for name, value in attrs.items():
106                self.file.write(' %s="%s"' % (escape(name), escape(value)))
107        
108        def open_element(self, element, attrs = {}):
109    
110            #
111            # 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            assert(self.indent_level >= 0)
131    
132            # see open_element() for an explanation
133            if self.element_open == 1:
134                self.element_open = 0
135                self.file.write("/>\n")
136            else:
137                self.file.write("%s</%s>\n" % (TAB*self.indent_level, element))
138    
139        def write_element(self, element, attrs = {}):
140            """write an element that won't need a closing tag"""
141            self.open_element(element, attrs)
142            self.close_element(element)
143    
144        def write_header(self):
145            """Write the XML header"""
146            self.file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
147            self.file.write('<!DOCTYPE session SYSTEM "thuban.dtd">\n')
148    
149        def write_session(self, session, attrs = None, namespaces = ()):
150            """Write the session and its contents
151    
152            By default, write a session element with the title attribute and
153            call write_map for each map contained in the session.
154    
155            The optional argument attrs is for additional attributes and, if
156            given, should be a mapping from attribute names to attribute
157            values. The values should not be XML-escaped yet.
158    
159            The optional argument namespaces, if given, should be a sequence
160            of (name, URI) pairs. The namespaces are written as namespace
161            attributes into the session element. This is mainly useful for
162            derived classes that need to store additional information in a
163            thuban session file.
164            """
165            if attrs is None:
166                attrs = {}
167            attrs["title"] = session.title
168            for name, uri in namespaces:
169                attrs["xmlns:" + name] = uri
170            self.open_element("session", attrs)
171            for map in session.Maps():
172                self.write_map(map)
173            self.close_element("session")
174    
175        def write_map(self, map):
176            """Write the map and its contents.
177    
178            By default, write a map element element with the title
179            attribute, call write_projection to write the projection
180            element, call write_layer for each layer contained in the map
181            and finally call write_label_layer to write the label layer.
182            """
183            write = self.file.write
184            self.open_element('map title="%s"' % escape(map.title))
185            self.write_projection(map.projection)
186          for layer in map.Layers():          for layer in map.Layers():
187              fill = layer.fill              self.write_layer(layer)
188              if fill is None:          self.write_label_layer(map.LabelLayer())
189                  fill = "None"          self.close_element('map')
190              else:  
191                  fill = fill.hex()      def write_projection(self, projection):
192              stroke = layer.stroke          """Write the projection.
193              if stroke is None:          """
194                  stroke = "None"          if projection and len(projection.params) > 0:
195              else:              self.open_element("projection")
196                  stroke = stroke.hex()              for param in projection.params:
197              write(('\t\t<layer title="%s" filename="%s"'                  self.write_element('parameter value="%s"' % escape(param))
198                     ' fill="%s" stroke="%s" stroke_width="%d"/>\n') %              self.close_element("projection")
199                    (escape(layer.title),  
200                     escape(relative_filename(dir, layer.filename)),      def write_layer(self, layer, attrs = None):
201                     fill, stroke, layer.stroke_width))          """Write the layer.
202          labels = map.LabelLayer().Labels()  
203            The optional argument attrs is for additional attributes and, if
204            given, should be a mapping from attribute names to attribute
205            values. The values should not be XML-escaped yet.
206            """
207            lc = layer.GetClassification()
208    
209            if attrs is None:
210                attrs = {}
211    
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            self.open_element("layer", attrs)
219            self.write_classification(layer)
220            self.close_element("layer")
221    
222        def write_classification(self, layer, attrs = None):
223            if attrs is None:
224                attrs = {}
225    
226            lc = layer.GetClassification()
227    
228            field = lc.GetField()
229    
230            #
231            # there isn't a classification of anything
232            # so don't do anything
233            #
234            if field is None: return
235    
236            attrs["field"] = field
237            self.open_element("classification", attrs)
238    
239    
240    #       self.open_element("clnull")
241    #       write_class_data(lc.GetDefaultData())
242    #       self.close_element("clnull")
243                
244            # just playing now with lambdas and dictionaries
245    
246            types = [[lambda p: 'clnull',
247                      lambda p: 'clnull'],
248                     [lambda p: 'clpoint value="%s"' %
249                                 str(p.GetValue()),
250                      lambda p: 'clpoint'],
251                     [lambda p: 'clrange min="%s" max="%s"' %
252                                 (str(p.GetMin()),
253                                  (str(p.GetMax()))),
254                      lambda p: 'clrange']]
255    
256            def write_class_group(group):
257                type = -1
258                if isinstance(group, ClassGroupDefault): type = 0
259                elif isinstance(group, ClassGroupSingleton): type = 1
260                elif isinstance(group, ClassGroupRange): type = 2
261                elif isinstance(group, ClassGroupMap):   type = 3
262                assert(type >= 0)
263    
264                if type <= 2:
265                    data = group.GetProperties()
266                    dict = {'stroke'      : data.GetStroke().hex(),
267                            'stroke_width': str(data.GetStrokeWidth()),
268                            'fill'        : data.GetFill().hex()}
269    
270                    self.open_element(types[type][0](group))
271                    self.write_element("cldata", dict)
272                    self.close_element(types[type][1](group))
273                else: pass # XXX: we need to handle maps
274    
275            for i in lc:
276                write_class_group(i)
277    
278    #       for i in lc:
279    #           t = i.GetType()
280    #           self.open_element(types[t][0](i))
281    #           write_class_data(i)
282    #           self.close_element(types[t][1](i))
283    
284    #       for p in lc:
285    #           type = p.GetType()
286    #           if p == ClassData.DEFAULT:
287    #               lopen = lclose = 'clnull'
288    #           elif p == ClassData.POINTS:
289    #               lopen = 'clpoint value="%s"' % escape(str(p.GetValue()))
290    #               lclose = 'clpoint'
291    #           elif p == ClassData.RANGES:
292    #               lopen = 'clrange min="%s" max="%s"'
293    #                   % (escape(str(p.GetMin())), escape(str(p.GetMax()))))
294    #               lclose = 'clrange'
295    
296    #           self.open_element(lopen)
297    #           write_class_data(p)
298    #           self.close_element(lclose)
299                
300    #       if lc.points != {}:
301    #           for p in lc.points.values():
302    #               self.open_element('clpoint value="%s"' %
303    #                   (escape(str(p.GetValue()))))
304    #               write_class_data(p)
305    #               self.close_element('clpoint')
306    #          
307    #       if lc.ranges != []:
308    #           for p in lc.ranges:
309    #               self.open_element('clrange min="%s" max="%s"'
310    #                   % (escape(str(p.GetMin())), escape(str(p.GetMax()))))
311    #               write_class_data(p)
312    #               self.close_element('clrange')
313    
314            self.close_element("classification")
315    
316        def write_label_layer(self, layer):
317            """Write the label layer.
318            """
319            labels = layer.Labels()
320          if labels:          if labels:
321              write('\t\t<labellayer>\n')              self.open_element('labellayer')
322              for label in labels:              for label in labels:
323                  write(('\t\t\t<label x="%g" y="%g" text="%s"'                  self.write_element(('label x="%g" y="%g" text="%s"'
324                         ' halign="%s" valign="%s"/>\n')                                      ' halign="%s" valign="%s"')
325                        % (label.x, label.y, label.text, label.halign,                                  % (label.x, label.y, label.text, label.halign,
326                           label.valign))                                     label.valign))
327              write('\t\t</labellayer>\n')              self.close_element('labellayer')
328          write('\t</map>\n')  
329      write('</session>\n')  
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        Saver.
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 = Saver
345        saver = saver_class(session)
346        saver.write(file)
347    
348      # after a successful save consider the session unmodified.      # after a successful save consider the session unmodified.
349      session.UnsetModified()      session.UnsetModified()

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26