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

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

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

revision 722 by jonathan, Thu Apr 24 10:12:29 2003 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]>
# Line 13  Parser for thuban session files. Line 13  Parser for thuban session files.
13    
14  __version__ = "$Revision$"  __version__ = "$Revision$"
15    
16  import sys, string, os  import string, os
17    
18  import xml.sax  import xml.sax
19  import xml.sax.handler  import xml.sax.handler
20  from xml.sax import make_parser, ErrorHandler, SAXNotRecognizedException  from xml.sax import make_parser, ErrorHandler, SAXNotRecognizedException
21    
22  from Thuban import _  from Thuban import _
 from Thuban.common import *  
23    
24  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
25       FIELDTYPE_STRING       FIELDTYPE_STRING
26    
27  from Thuban.Model.session import Session  from Thuban.Model.session import Session
28  from Thuban.Model.map import Map  from Thuban.Model.map import Map
29  from Thuban.Model.layer import Layer  from Thuban.Model.layer import Layer, RasterLayer
30  from Thuban.Model.color import Color  from Thuban.Model.color import Color
31  from Thuban.Model.proj import Projection  from Thuban.Model.proj import Projection
32    from Thuban.Model.range import Range
33  from Thuban.Model.classification import Classification, \  from Thuban.Model.classification import Classification, \
34      ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \      ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
35      ClassGroupProperties      ClassGroupProperties
36    from Thuban.Model.data import DerivedShapeStore, ShapefileStore
37    from Thuban.Model.table import DBFTable
38    from Thuban.Model.transientdb import TransientJoinedTable
39    
40    class LoadError(Exception):
41        pass
42    
43    from Thuban.Model.xmlreader import XMLReader
44    import resource
45    
46  def parse_color(color):  def parse_color(color):
47      """Return the color object for the string color.      """Return the color object for the string color.
# Line 57  def parse_color(color): Line 65  def parse_color(color):
65          raise ValueError(_("Invalid color specification %s") % color)          raise ValueError(_("Invalid color specification %s") % color)
66      return result      return result
67    
68    class AttrDesc:
69    
70  class XMLReader(xml.sax.handler.ContentHandler):      def __init__(self, name, required = False, default = "",
71                     conversion = None):
72      # Dictionary mapping element names (or (URI, element name) pairs for          if not isinstance(name, tuple):
73      # documents using namespaces) to method names. The methods should              fullname = (None, name)
     # accept the same parameters as the startElement (or startElementNS)  
     # methods. The start_dispatcher is used by the default startElement  
     # and startElementNS methods to call a method for the open tag of an  
     # element.  
     start_dispatcher = {}  
   
     # end_dispatcher works just like start_dispatcher but it's used by  
     # endElement and endElementNS. The method whose names it maps to  
     # should accept the same parameters as endElement and endElementNS.  
     end_dispatcher = {}  
   
   
     def __init__(self):  
         self.chars = ''  
         self.__parser = None  
         self.__directory = ""  
         self.__dispatchers = {}  
   
     def read(self, file_or_filename):  
   
         if hasattr(file_or_filename, "read"):  
             # it's a file object  
             self.__directory = ""  
             self.__file = file_or_filename  
74          else:          else:
75              filename = file_or_filename              fullname = name
76              self.__directory = os.path.dirname(filename)              name = name[1]
77              self.__file = open(filename)          self.name = name
78            self.fullname = fullname
79          if self.__parser is None:          self.required = required
80              self.__parser = make_parser()          self.default = default
81              self.__parser.setContentHandler(self)          self.conversion = conversion
             self.__parser.setErrorHandler(ErrorHandler())  
             self.__parser.setFeature(xml.sax.handler.feature_namespaces, 1)  
   
             #  
             # Well, this isn't pretty, but it appears that if you  
             # use Python 2.2 without the site-package _xmlplus then  
             # the following will fail, and without them it will work.  
             # However, if you do have the site-package and you don't  
             # call these functions, the reader raises an exception  
             #  
             # The reason we set these to 0 in the first place is  
             # because there is an unresolved issue with external  
             # entities causing an exception in the reader  
             #  
             try:  
                 self.__parser.setFeature(xml.sax.handler.feature_validation,0)  
                 self.__parser.setFeature(xml.sax.handler.feature_external_ges,0)  
                 self.__parser.setFeature(xml.sax.handler.feature_external_pes,0)  
             except SAXNotRecognizedException:  
                 pass  
   
         self.__parser.parse(self.__file)  
   
         self.close()  
   
     def close(self):  
         self.__file.close()  
           
     def GetFileName(self):  
         if hasattr(self.__file, "name"):  
             return self.__file.name  
   
         return ""  
   
     def GetDirectory(self):  
         return self.__directory  
   
82    
83      def AddDispatchers(self, dict):          # set by the SessionLoader's check_attrs method
84          """Add the function names that should be used to process XML tags.          self.value = None
85    
         dict -- a dictionary whose keys are XML tag strings and whose values  
                 are pairs of strings such that the first string is  
                 the name of the function that should be called when the  
                 XML tag opens and the second string is the name of the  
                 function that should be called when the XML tag closes.  
                 If a pair element is None, no function is called.  
         """  
   
         self.__dispatchers.update(dict)  
   
     def startElementNS(self, name, qname, attrs):  
         """Call the method given for name in self.start_dispatcher  
         """  
         if name[0] is None:  
             method_name = self.__dispatchers.get(name[1])  
         else:  
             # Dispatch with namespace  
             method_name = self.__dispatchers.get(name)  
         if method_name is not None and method_name[0] is not None:  
             getattr(self, method_name[0])(name, qname, attrs)  
   
     def endElementNS(self, name, qname):  
         """Call the method given for name in self.end_dispatcher  
         """  
         if name[0] is None:  
             method_name = self.__dispatchers.get(name[1])  
         else:  
             # Dispatch with namespace  
             method_name = self.__dispatchers.get(name)  
         if method_name is not None and method_name[1] is not None:  
             getattr(self, method_name[1])(name, qname)  
86    
87  class SessionLoader(XMLReader):  class SessionLoader(XMLReader):
88    
# Line 177  class SessionLoader(XMLReader): Line 94  class SessionLoader(XMLReader):
94          self.aMap = None          self.aMap = None
95          self.aLayer = None          self.aLayer = None
96    
97          XMLReader.AddDispatchers(self,          # Map ids used in the thuban file to the corresponding objects
98              {'session'       : ("start_session",        "end_session"),          # in the session
99               'map'           : ("start_map",            "end_map"),          self.idmap = {}
100               'projection'    : ("start_projection",     "end_projection"),  
101               'parameter'     : ("start_parameter",      None),          dispatchers = {
102               'layer'         : ("start_layer",          "end_layer"),              'session'       : ("start_session",        "end_session"),
103               'classification': ("start_classification", "end_classification"),              'fileshapesource': ("start_fileshapesource", None),
104               'clnull'        : ("start_clnull",         "end_clnull"),              'derivedshapesource': ("start_derivedshapesource", None),
105               'clpoint'       : ("start_clpoint",        "end_clpoint"),              'filetable': ("start_filetable", None),
106               'clrange'       : ("start_clrange",        "end_clrange"),              'jointable': ("start_jointable", None),
107               'cldata'        : ("start_cldata",         "end_cldata"),  
108               'table'         : ("start_table",          "end_table"),              'map'           : ("start_map",            "end_map"),
109               'labellayer'    : ("start_labellayer",     None),              'projection'    : ("start_projection",     "end_projection"),
110               'label'         : ("start_label",          None)})              'parameter'     : ("start_parameter",      None),
111                'layer'         : ("start_layer",          "end_layer"),
112                'rasterlayer'   : ("start_rasterlayer",    "end_rasterlayer"),
113                'classification': ("start_classification", "end_classification"),
114                'clnull'        : ("start_clnull",         "end_clnull"),
115                'clpoint'       : ("start_clpoint",        "end_clpoint"),
116                'clrange'       : ("start_clrange",        "end_clrange"),
117                'cldata'        : ("start_cldata",         "end_cldata"),
118                'table'         : ("start_table",          "end_table"),
119                'labellayer'    : ("start_labellayer",     None),
120                'label'         : ("start_label",          None)}
121    
122            # all dispatchers should be used for the 0.8 namespace
123            xmlns = "http://thuban.intevation.org/dtds/thuban-0.8.dtd"
124            for key, value in dispatchers.items():
125                dispatchers[(xmlns, key)] = value
126    
127            XMLReader.AddDispatchers(self, dispatchers)
128    
129      def start_session(self, name, qname, attrs):      def start_session(self, name, qname, attrs):
130          self.theSession = Session(attrs.get((None, 'title'), None))          self.theSession = Session(self.encode(attrs.get((None, 'title'),
131                                                            None)))
132    
133      def end_session(self, name, qname):      def end_session(self, name, qname):
134          pass          pass
135    
136        def check_attrs(self, element, attrs, descr):
137            """Check and convert some of the attributes of an element
138    
139            Parameters:
140               element -- The element name
141               attrs -- The attrs mapping as passed to the start_* methods
142               descr -- Sequence of attribute descriptions (AttrDesc instances)
143    
144            Return a dictionary containig normalized versions of the
145            attributes described in descr. The keys of that dictionary are
146            the name attributes of the attribute descriptions. The attrs
147            dictionary will not be modified.
148    
149            If the attribute is required, i.e. the 'required' attribute of
150            the descrtiption is true, but it is not in attrs, raise a
151            LoadError.
152    
153            If the attribute has a default value and it is not present in
154            attrs, use that default value as the value in the returned dict.
155    
156            If a conversion is specified, convert the value before putting
157            it into the returned dict. The following conversions are
158            available:
159    
160               'filename' -- The attribute is a filename.
161    
162                             If the filename is a relative name, interpret
163                             it relative to the directory containing the
164                             .thuban file and make it an absolute name
165    
166               'shapestore' -- The attribute is the ID of a shapestore
167                               defined earlier in the .thuban file. Look it
168                               up self.idmap
169    
170               'table' -- The attribute is the ID of a table or shapestore
171                          defined earlier in the .thuban file. Look it up
172                          self.idmap. If it's the ID of a shapestore the
173                          value will be the table of the shapestore.
174            """
175            normalized = {}
176    
177            for d in descr:
178                if d.required and not attrs.has_key(d.fullname):
179                    pass
180                #raise LoadError("Element %s requires an attribute %r"
181                #                    % (element, d.name))
182                value = attrs.get(d.fullname, d.default)
183    
184                if d.conversion == "shapesource":
185                    if value in self.idmap:
186                        value = self.idmap[value]
187                    else:
188                        raise LoadError("Element %s requires an already defined ID"
189                                        " in attribute %r"
190                                        % (element, d.name))
191                elif d.conversion == "table":
192                    if value in self.idmap:
193                        value = self.idmap[value]
194                        if isinstance(value, ShapefileStore):
195                            value = value.Table()
196                    else:
197                        raise LoadError("Element %s requires an already defined ID"
198                                        " in attribute %r"
199                                        % (element, d.name))
200                elif d.conversion == "filename":
201                    value = os.path.abspath(os.path.join(self.GetDirectory(),
202                                                         value))
203    
204                normalized[d.name] = value
205            return normalized
206    
207        def start_fileshapesource(self, name, qname, attrs):
208            attrs = self.check_attrs(name, attrs,
209                                      [AttrDesc("id", True),
210                                       AttrDesc("filename", True,
211                                                conversion = "filename"),
212                                       AttrDesc("filetype", True)])
213            ID = attrs["id"]
214            filename = attrs["filename"]
215            filetype = attrs["filetype"]
216            if filetype != "shapefile":
217                raise LoadError("shapesource filetype %r not supported" % filetype)
218            self.idmap[ID] = self.theSession.OpenShapefile(filename)
219    
220        def start_derivedshapesource(self, name, qname, attrs):
221            attrs = self.check_attrs(name, attrs,
222                                     [AttrDesc("id", True),
223                                      AttrDesc("shapesource", True,
224                                               conversion = "shapesource"),
225                                      AttrDesc("table", True, conversion="table")])
226            self.idmap[attrs["id"]] = DerivedShapeStore(attrs["shapesource"],
227                                                        attrs["table"])
228    
229        def start_filetable(self, name, qname, attrs):
230            attrs = self.check_attrs(name, attrs,
231                                     [AttrDesc("id", True),
232                                      AttrDesc("title", True),
233                                      AttrDesc("filename", True,
234                                               conversion = "filename"),
235                                      AttrDesc("filetype")])
236            filetype = attrs["filetype"]
237            if filetype != "DBF":
238                raise LoadError("shapesource filetype %r not supported" % filetype)
239            table = DBFTable(attrs["filename"])
240            table.SetTitle(attrs["title"])
241            self.idmap[attrs["id"]] = self.theSession.AddTable(table)
242    
243        def start_jointable(self, name, qname, attrs):
244            attrs = self.check_attrs(name, attrs,
245                                     [AttrDesc("id", True),
246                                      AttrDesc("title", True),
247                                      AttrDesc("left", True, conversion="table"),
248                                      AttrDesc("leftcolumn", True),
249                                      AttrDesc("right", True, conversion="table"),
250                                      AttrDesc("rightcolumn")])
251            table = TransientJoinedTable(self.theSession.TransientDB(),
252                                         attrs["left"], attrs["leftcolumn"],
253                                         attrs["right"], attrs["rightcolumn"])
254            table.SetTitle(attrs["title"])
255            self.idmap[attrs["id"]] = self.theSession.AddTable(table)
256    
257      def start_map(self, name, qname, attrs):      def start_map(self, name, qname, attrs):
258          """Start a map."""          """Start a map."""
259          self.aMap = Map(attrs.get((None, 'title'), None))          self.aMap = Map(attrs.get((None, 'title'), None))
260    
261      def end_map(self, name, qname):      def end_map(self, name, qname):
262          self.theSession.AddMap(self.aMap)          self.theSession.AddMap(self.aMap)
263            self.aMap = None
264    
265      def start_projection(self, name, qname, attrs):      def start_projection(self, name, qname, attrs):
266            self.ProjectionName = self.encode(attrs.get((None, 'name'), None))
267          self.ProjectionParams = [ ]          self.ProjectionParams = [ ]
268    
269      def end_projection(self, name, qname):      def end_projection(self, name, qname):
270          self.aMap.SetProjection(Projection(self.ProjectionParams))          if self.aLayer is not None:
271                obj = self.aLayer
272            elif self.aMap is not None:
273                obj = self.aMap
274            else:
275                assert False, "projection tag out of context"
276                pass
277    
278            obj.SetProjection(
279                Projection(self.ProjectionParams, self.ProjectionName))
280    
281      def start_parameter(self, name, qname, attrs):      def start_parameter(self, name, qname, attrs):
282          s = attrs.get((None, 'value'))          s = attrs.get((None, 'value'))
# Line 223  class SessionLoader(XMLReader): Line 290  class SessionLoader(XMLReader):
290          attrs which may be a dictionary as well as the normal SAX attrs          attrs which may be a dictionary as well as the normal SAX attrs
291          object and bind it to self.aLayer.          object and bind it to self.aLayer.
292          """          """
293          title = attrs.get((None, 'title'), "")          title = self.encode(attrs.get((None, 'title'), ""))
294          filename = attrs.get((None, 'filename'), "")          filename = attrs.get((None, 'filename'), "")
295          filename = os.path.join(self.GetDirectory(), filename)          filename = os.path.join(self.GetDirectory(), filename)
296            filename = self.encode(filename)
297            visible  = self.encode(attrs.get((None, 'visible'), "true")) != "false"
298          fill = parse_color(attrs.get((None, 'fill'), "None"))          fill = parse_color(attrs.get((None, 'fill'), "None"))
299          stroke = parse_color(attrs.get((None, 'stroke'), "#000000"))          stroke = parse_color(attrs.get((None, 'stroke'), "#000000"))
300          stroke_width = int(attrs.get((None, 'stroke_width'), "1"))          stroke_width = int(attrs.get((None, 'stroke_width'), "1"))
301          self.aLayer = layer_class(title, filename, fill = fill,          if attrs.has_key((None, "shapestore")):
302                                    stroke = stroke, lineWidth = stroke_width)              store = self.idmap[attrs[(None, "shapestore")]]
303            else:
304                store = self.theSession.OpenShapefile(filename)
305            self.aLayer = layer_class(title, store,
306                                      fill = fill, stroke = stroke,
307                                      lineWidth = stroke_width,
308                                      visible = visible)
309    
310      def end_layer(self, name, qname):      def end_layer(self, name, qname):
311          self.aMap.AddLayer(self.aLayer)          self.aMap.AddLayer(self.aLayer)
312            self.aLayer = None
313    
314        def start_rasterlayer(self, name, qname, attrs, layer_class = RasterLayer):
315            title = self.encode(attrs.get((None, 'title'), ""))
316            filename = attrs.get((None, 'filename'), "")
317            filename = os.path.join(self.GetDirectory(), filename)
318            filename = self.encode(filename)
319            visible  = self.encode(attrs.get((None, 'visible'), "true")) != "false"
320    
321            self.aLayer = layer_class(title, filename, visible = visible)
322    
323        def end_rasterlayer(self, name, qname):
324            self.aMap.AddLayer(self.aLayer)
325            self.aLayer = None
326    
327      def start_classification(self, name, qname, attrs):      def start_classification(self, name, qname, attrs):
328          field = attrs.get((None, 'field'), None)          field = attrs.get((None, 'field'), None)
# Line 255  class SessionLoader(XMLReader): Line 344  class SessionLoader(XMLReader):
344    
345          self.aLayer.GetClassification().SetField(field)          self.aLayer.GetClassification().SetField(field)
346    
   
347      def end_classification(self, name, qname):      def end_classification(self, name, qname):
348          pass          pass
349    
350      def start_clnull(self, name, qname, attrs):      def start_clnull(self, name, qname, attrs):
351          self.cl_group = ClassGroupDefault()          self.cl_group = ClassGroupDefault()
352          self.cl_group.SetLabel(attrs.get((None, 'label'), ""))          self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
353          self.cl_prop = ClassGroupProperties()          self.cl_prop = ClassGroupProperties()
354    
355      def end_clnull(self, name, qname):      def end_clnull(self, name, qname):
# Line 272  class SessionLoader(XMLReader): Line 360  class SessionLoader(XMLReader):
360      def start_clpoint(self, name, qname, attrs):      def start_clpoint(self, name, qname, attrs):
361          attrib_value = attrs.get((None, 'value'), "0")          attrib_value = attrs.get((None, 'value'), "0")
362    
         #try:  
             #value  = Str2Num(attrib_value)  
         #except:  
             #value  = attrib_value  
   
363          value = self.conv(attrib_value)          value = self.conv(attrib_value)
364    
365          self.cl_group = ClassGroupSingleton(value)          self.cl_group = ClassGroupSingleton(value)
366          self.cl_group.SetLabel(attrs.get((None, 'label'), ""))          self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
367          self.cl_prop = ClassGroupProperties()          self.cl_prop = ClassGroupProperties()
368    
369    
# Line 291  class SessionLoader(XMLReader): Line 374  class SessionLoader(XMLReader):
374    
375      def start_clrange(self, name, qname, attrs):      def start_clrange(self, name, qname, attrs):
376    
377            range = attrs.get((None, 'range'), None)
378            # for backward compatibility (min/max are not saved)
379            min   = attrs.get((None, 'min'), None)
380            max   = attrs.get((None, 'max'), None)
381    
382          try:          try:
383              min = self.conv(attrs.get((None, 'min'), "0"))              if range is not None:
384              max = self.conv(attrs.get((None, 'max'), "0"))                  self.cl_group = ClassGroupRange(Range(range))
385              #min = Str2Num(attrs.get((None, 'min'), "0"))              elif min is not None and max is not None:
386              #max = Str2Num(attrs.get((None, 'max'), "0"))                  self.cl_group = ClassGroupRange(self.conv(min), self.conv(max))
387                else:
388                    self.cl_group = ClassGroupRange(Range(None))
389    
390          except ValueError:          except ValueError:
391              raise ValueError(_("Classification range is not a number!"))              raise ValueError(_("Classification range is not a number!"))
392    
         self.cl_group = ClassGroupRange(min, max)  
393          self.cl_group.SetLabel(attrs.get((None, 'label'), ""))          self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
394          self.cl_prop = ClassGroupProperties()          self.cl_prop = ClassGroupProperties()
395    
# Line 319  class SessionLoader(XMLReader): Line 409  class SessionLoader(XMLReader):
409      def end_cldata(self, name, qname):      def end_cldata(self, name, qname):
410          pass          pass
411    
     def start_table(self, name, qname, attrs):  
         #print "table title: %s" % attrs.get('title', None)  
         pass  
   
     def end_table(self, name, qname):  
         pass  
   
412      def start_labellayer(self, name, qname, attrs):      def start_labellayer(self, name, qname, attrs):
413          self.aLayer = self.aMap.LabelLayer()          self.aLayer = self.aMap.LabelLayer()
414    
415      def start_label(self, name, qname, attrs):      def start_label(self, name, qname, attrs):
416          x = float(attrs[(None, 'x')])          x = float(attrs[(None, 'x')])
417          y = float(attrs[(None, 'y')])          y = float(attrs[(None, 'y')])
418          text = attrs[(None, 'text')]          text = self.encode(attrs[(None, 'text')])
419          halign = attrs[(None, 'halign')]          halign = attrs[(None, 'halign')]
420          valign = attrs[(None, 'valign')]          valign = attrs[(None, 'valign')]
421          self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)          self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26