/[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 201 by bh, Mon Jul 8 10:50:53 2002 UTC revision 1268 by bh, Fri Jun 20 16:10:12 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002 by Intevation GmbH  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
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 13  Functions to save a session to a file Line 14  Functions to save a session to a file
14  __version__ = "$Revision$"  __version__ = "$Revision$"
15    
16  import os  import os
 import string  
17    
18  import Thuban.Lib.fileutil  import Thuban.Lib.fileutil
19    
20    from Thuban.Model.color import Color
21    from Thuban.Model.layer import Layer, RasterLayer
22    
23    from Thuban.Model.classification import \
24        ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap
25    from Thuban.Model.transientdb import AutoTransientTable, TransientJoinedTable
26    from Thuban.Model.table import DBFTable
27    from Thuban.Model.data import DerivedShapeStore, ShapefileStore
28    
29    from Thuban.Model.xmlwriter import XMLWriter
30    
31  def relative_filename(dir, filename):  def relative_filename(dir, filename):
32      """Return a filename relative to dir for the absolute file name absname.      """Return a filename relative to dir for the absolute file name absname.
33    
# Line 29  def relative_filename(dir, filename): Line 40  def relative_filename(dir, filename):
40      else:      else:
41          return filename          return filename
42    
43  def escape(data):  
44      """Escape &, \", ', <, and > in a string of data.  def sort_data_stores(stores):
45        """Return a topologically sorted version of the sequence of data containers
46    
47        The list is sorted so that data containers that depend on other data
48        containers have higher indexes than the containers they depend on.
49      """      """
50      data = string.replace(data, "&", "&amp;")      if not stores:
51      data = string.replace(data, "<", "&lt;")          return []
52      data = string.replace(data, ">", "&gt;")      processed = {}
53      data = string.replace(data, '"', "&quot;")      result = []
54      data = string.replace(data, "'", "&apos;")      todo = stores[:]
55      return data      while todo:
56            # It doesn't really matter which if the items of todo is
57  def save_session(session, filename):          # processed next, but if we take the first one, the order is
58      """Save the session session to the file given by filename"""          # preserved to some degree which makes writing some of the test
59      dir = os.path.dirname(filename)          # cases easier.
60      file = open(filename, 'w')          container = todo.pop(0)
61      write = file.write          if id(container) in processed:
62      write('<?xml version="1.0" encoding="UTF-8"?>\n')              continue
63      write('<!DOCTYPE session SYSTEM "thuban.dtd">\n')          deps = [dep for dep in container.Dependencies()
64      write('<session title="%s">\n' % escape(session.title))                      if id(dep) not in processed]
65      for map in session.Maps():          if deps:
66          write('\t<map title="%s">\n' % escape(map.title))              todo.append(container)
67          if map.projection and len(map.projection.params) > 0:              todo.extend(deps)
68              write('\t\t<projection>\n')          else:
69              for param in map.projection.params:              result.append(container)
70                  write('\t\t\t<parameter value="%s"/>\n' % escape(param))              processed[id(container)] = 1
71              write('\t\t</projection>\n')      return result
72    
73          for layer in map.Layers():  
74              fill = layer.fill  class SessionSaver(XMLWriter):
75              if fill is None:  
76                  fill = "None"      """Class to serialize a session into an XML file.
77    
78        Applications built on top of Thuban may derive from this class and
79        override or extend the methods to save additional information. This
80        additional information should take the form of additional attributes
81        or elements whose names are prefixed with a namespace. To define a
82        namespace derived classes should extend the write_session method to
83        pass the namespaces to the default implementation.
84        """
85    
86    
87        def __init__(self, session):
88            XMLWriter.__init__(self)
89            self.session = session
90            # Map object ids to the ids used in the thuban files
91            self.idmap = {}
92    
93        def get_id(self, obj):
94            """Return the id used in the thuban file for the object obj"""
95            return self.idmap.get(id(obj))
96    
97        def define_id(self, obj, value = None):
98            if value is None:
99                value = "D" + str(id(obj))
100            self.idmap[id(obj)] = value
101            return value
102    
103        def has_id(self, obj):
104            return self.idmap.has_key(id(obj))
105    
106        def write(self, file_or_filename):
107            XMLWriter.write(self, file_or_filename)
108    
109            self.write_header("session", "thuban-0.8.dtd")
110            self.write_session(self.session)
111            self.close()
112    
113        def write_session(self, session, attrs = None, namespaces = ()):
114            """Write the session and its contents
115    
116            By default, write a session element with the title attribute and
117            call write_map for each map contained in the session.
118    
119            The optional argument attrs is for additional attributes and, if
120            given, should be a mapping from attribute names to attribute
121            values. The values should not be XML-escaped yet.
122    
123            The optional argument namespaces, if given, should be a sequence
124            of (name, URI) pairs. The namespaces are written as namespace
125            attributes into the session element. This is mainly useful for
126            derived classes that need to store additional information in a
127            thuban session file.
128            """
129            if attrs is None:
130                attrs = {}
131            attrs["title"] = session.title
132            for name, uri in namespaces:
133                attrs["xmlns:" + name] = uri
134            # default name space
135            attrs["xmlns"] = \
136                   "http://thuban.intevation.org/dtds/thuban-0.8.dtd"
137            self.open_element("session", attrs)
138            self.write_data_containers(session)
139            for map in session.Maps():
140                self.write_map(map)
141            self.close_element("session")
142    
143        def write_data_containers(self, session):
144            containers = sort_data_stores(session.DataContainers())
145            for container in containers:
146                if isinstance(container, AutoTransientTable):
147                    # AutoTransientTable instances are invisible in the
148                    # thuban files. They're only used internally. To make
149                    # sure that containers depending on AutoTransientTable
150                    # instances refer to the right real containers we give
151                    # the AutoTransientTable instances the same id as the
152                    # source they depend on.
153                    self.define_id(container,
154                                   self.get_id(container.Dependencies()[0]))
155                    continue
156    
157                idvalue = self.define_id(container)
158                if isinstance(container, ShapefileStore):
159                    self.define_id(container.Table(), idvalue)
160                    filename = relative_filename(self.dir, container.FileName())
161                    self.write_element("fileshapesource",
162                                       {"id": idvalue, "filename": filename,
163                                        "filetype": "shapefile"})
164                elif isinstance(container, DerivedShapeStore):
165                    shapesource, table = container.Dependencies()
166                    self.write_element("derivedshapesource",
167                                       {"id": idvalue,
168                                        "shapesource": self.get_id(shapesource),
169                                        "table": self.get_id(table)})
170                elif isinstance(container, DBFTable):
171                    filename = relative_filename(self.dir, container.FileName())
172                    self.write_element("filetable",
173                                       {"id": idvalue,
174                                        "title": container.Title(),
175                                        "filename": filename,
176                                        "filetype": "DBF"})
177                elif isinstance(container, TransientJoinedTable):
178                    left, right = container.Dependencies()
179                    left_field = container.left_field
180                    right_field = container.right_field
181                    self.write_element("jointable",
182                                       {"id": idvalue,
183                                        "title": container.Title(),
184                                        "right": self.get_id(right),
185                                        "rightcolumn": right_field,
186                                        "left": self.get_id(left),
187                                        "leftcolumn": left_field})
188              else:              else:
189                  fill = fill.hex()                  raise ValueError("Can't handle container %r" % container)
190              stroke = layer.stroke  
191              if stroke is None:  
192                  stroke = "None"      def write_map(self, map):
193            """Write the map and its contents.
194    
195            By default, write a map element element with the title
196            attribute, call write_projection to write the projection
197            element, call write_layer for each layer contained in the map
198            and finally call write_label_layer to write the label layer.
199            """
200            self.open_element('map title="%s"' % self.encode(map.title))
201            self.write_projection(map.projection)
202            for layer in map.Layers():
203                self.write_layer(layer)
204            self.write_label_layer(map.LabelLayer())
205            self.close_element('map')
206    
207        def write_projection(self, projection):
208            """Write the projection.
209            """
210            if projection and len(projection.params) > 0:
211                self.open_element("projection", {"name": projection.GetName()})
212                for param in projection.params:
213                    self.write_element('parameter value="%s"' %
214                                       self.encode(param))
215                self.close_element("projection")
216    
217        def write_layer(self, layer, attrs = None):
218            """Write the layer.
219    
220            The optional argument attrs is for additional attributes and, if
221            given, should be a mapping from attribute names to attribute
222            values. The values should not be XML-escaped yet.
223            """
224    
225            if attrs is None:
226                attrs = {}
227    
228            attrs["title"]   = layer.title
229            attrs["visible"] = ("false", "true")[int(layer.Visible())]
230    
231            if isinstance(layer, Layer):
232                attrs["shapestore"]   = self.get_id(layer.ShapeStore())
233    
234                lc = layer.GetClassification()
235                attrs["stroke"] = lc.GetDefaultLineColor().hex()
236                attrs["stroke_width"] = str(lc.GetDefaultLineWidth())
237                attrs["fill"] = lc.GetDefaultFill().hex()
238    
239                self.open_element("layer", attrs)
240                self.write_projection(layer.GetProjection())
241                self.write_classification(layer)
242                self.close_element("layer")
243            elif isinstance(layer, RasterLayer):
244                attrs["filename"] = relative_filename(self.dir, layer.filename)
245                self.open_element("rasterlayer", attrs)
246                self.write_projection(layer.GetProjection())
247                self.close_element("rasterlayer")
248    
249        def write_classification(self, layer, attrs = None):
250            """Write Classification information."""
251    
252            if attrs is None:
253                attrs = {}
254    
255            lc = layer.GetClassification()
256    
257            field = lc.GetField()
258    
259            #
260            # there isn't a classification of anything so do nothing
261            #
262            if field is None: return
263    
264            attrs["field"] = field
265            attrs["field_type"] = str(lc.GetFieldType())
266            self.open_element("classification", attrs)
267    
268            for g in lc:
269                if isinstance(g, ClassGroupDefault):
270                    open_el  = 'clnull label="%s"' % self.encode(g.GetLabel())
271                    close_el = 'clnull'
272                elif isinstance(g, ClassGroupSingleton):
273                    open_el  = 'clpoint label="%s" value="%s"' \
274                               % (self.encode(g.GetLabel()), str(g.GetValue()))
275                    close_el = 'clpoint'
276                elif isinstance(g, ClassGroupRange):
277                    open_el  = 'clrange label="%s" range="%s"' \
278                              % (self.encode(g.GetLabel()), str(g.GetRange()))
279                    close_el = 'clrange'
280              else:              else:
281                  stroke = stroke.hex()                  assert False, _("Unsupported group type in classification")
282              write(('\t\t<layer title="%s" filename="%s"'                  continue
283                     ' fill="%s" stroke="%s" stroke_width="%d"/>\n') %  
284                    (escape(layer.title),              data = g.GetProperties()
285                     escape(relative_filename(dir, layer.filename)),              dict = {'stroke'      : data.GetLineColor().hex(),
286                     fill, stroke, layer.stroke_width))                      'stroke_width': str(data.GetLineWidth()),
287          labels = map.LabelLayer().Labels()                      'fill'        : data.GetFill().hex()}
288    
289                self.open_element(open_el)
290                self.write_element("cldata", dict)
291                self.close_element(close_el)
292    
293            self.close_element("classification")
294    
295        def write_label_layer(self, layer):
296            """Write the label layer.
297            """
298            labels = layer.Labels()
299          if labels:          if labels:
300              write('\t\t<labellayer>\n')              self.open_element('labellayer')
301              for label in labels:              for label in labels:
302                  write(('\t\t\t<label x="%g" y="%g" text="%s"'                  self.write_element(('label x="%g" y="%g" text="%s"'
303                         ' halign="%s" valign="%s"/>\n')                                      ' halign="%s" valign="%s"')
304                        % (label.x, label.y, label.text, label.halign,                                  % (label.x, label.y,
305                           label.valign))                                     self.encode(label.text),
306              write('\t\t</labellayer>\n')                                     label.halign,
307          write('\t</map>\n')                                     label.valign))
308      write('</session>\n')              self.close_element('labellayer')
309    
310    
311    
312    def save_session(session, file, saver_class = None):
313        """Save the session session to a file.
314    
315        The file argument may either be a filename or an open file object.
316    
317        The optional argument saver_class is the class to use to serialize
318        the session. By default or if it's None, the saver class will be
319        SessionSaver.
320    
321        If writing the session is successful call the session's
322        UnsetModified method
323        """
324        if saver_class is None:
325            saver_class = SessionSaver
326        saver = saver_class(session)
327        saver.write(file)
328    
329      # after a successful save consider the session unmodified.      # after a successful save consider the session unmodified.
330      session.UnsetModified()      session.UnsetModified()

Legend:
Removed from v.201  
changed lines
  Added in v.1268

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26