/[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 75 by bh, Mon Feb 4 19:22:22 2002 UTC revision 694 by jonathan, Wed Apr 16 16:39:18 2003 UTC
# Line 1  Line 1 
1  # Copyright (C) 2001, 2002 by Intevation GmbH  # Copyright (C) 2001, 2002 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jan-Oliver Wagner <[email protected]>  # 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)  # This program is free software under the GPL (>=v2)
8  # Read the file COPYING coming with GRASS for details.  # Read the file COPYING coming with GRASS for details.
# Line 12  Parser for thuban session files. Line 14  Parser for thuban session files.
14  __version__ = "$Revision$"  __version__ = "$Revision$"
15    
16  import sys, string, os  import sys, string, os
17    
18    import xml.sax
19    import xml.sax.handler
20    from xml.sax import make_parser, ErrorHandler, SAXNotRecognizedException
21    
22    from Thuban import _
23    from Thuban.common import *
24    
25    from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
26         FIELDTYPE_STRING
27    
28  from Thuban.Model.session import Session  from Thuban.Model.session import Session
29  from Thuban.Model.map import Map  from Thuban.Model.map import Map
30  from Thuban.Model.layer import Layer  from Thuban.Model.layer import Layer
31  from Thuban.Model.color import Color  from Thuban.Model.color import Color
32  from Thuban.Model.proj import Projection  from Thuban.Model.proj import Projection
33    from Thuban.Model.classification import Classification, \
34  oldPython=0      ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
35        ClassGroupProperties
 if not sys.__dict__.has_key("version_info"):  
     # We can assume to have python 1.5.2 or lower here now  
     oldPython=1  
   
 if oldPython:  
     try:  
         from xml.sax.saxexts import make_parser  
         from xml.sax.saxlib import HandlerBase  
         from xml.sax import saxutils  
     except ImportError:  
         sys.stdout.write(("You need to have Python-XML installed or"  
                           " a modern Python!\n"  
                           "Check www.python.org/sigs/xml-sig/\n\n"))  
         raise  
 else:  
     # Do the python 2.0 standard xml thing and map it on the old names  
     import xml.sax  
     import xml.sax.handler  
     HandlerBase=xml.sax.handler.ContentHandler  
     from xml.sax import make_parser  
   
 class testSAXContentHandler(HandlerBase):  
 # SAX compliant  
     def characters(self, ch, start, length):  
         pass  
       
 def test_for_broken_SAX():  
     ch=testSAXContentHandler()  
     try:  
         xml.sax.parseString("""<?xml version="1.0"?>  
             <child1 name="paul">Text goes here</child1>  
         """,ch)  
     except TypeError:  
         return 1  
     return 0  
36    
37    
38  def parse_color(color):  def parse_color(color):
39      """      """Return the color object for the string color.
40      Return the color object for the string color. Color may be either  
41      'None' or of the form '#RRGGBB' in the usual HTML color notation      Color may be either 'None' or of the form '#RRGGBB' in the usual
42        HTML color notation
43      """      """
44      color = string.strip(color)      color = string.strip(color)
45      if color == "None":      if color == "None":
46          result = None          result = Color.Transparent
47      elif color[0] == '#':      elif color[0] == '#':
48          if len(color) == 7:          if len(color) == 7:
49              r = string.atoi(color[1:3], 16) / 255.0              r = string.atoi(color[1:3], 16) / 255.0
# Line 72  def parse_color(color): Line 51  def parse_color(color):
51              b = string.atoi(color[5:7], 16) / 255.0              b = string.atoi(color[5:7], 16) / 255.0
52              result = Color(r, g, b)              result = Color(r, g, b)
53          else:          else:
54              raise ValueError("Invalid hexadecimal color specification %s"              raise ValueError(_("Invalid hexadecimal color specification %s")
55                               % color)                               % color)
56      else:      else:
57          raise ValueError("Invalid color specification %s" % color)          raise ValueError(_("Invalid color specification %s") % color)
58      return result      return result
59    
60    
61  class ProcessSession(HandlerBase):  class XMLProcessor(xml.sax.handler.ContentHandler):
62    
63        # Dictionary mapping element names (or (URI, element name) pairs for
64        # documents using namespaces) to method names. The methods should
65        # accept the same parameters as the startElement (or startElementNS)
66        # methods. The start_dispatcher is used by the default startElement
67        # and startElementNS methods to call a method for the open tag of an
68        # element.
69        start_dispatcher = {}
70    
71        # end_dispatcher works just like start_dispatcher but it's used by
72        # endElement and endElementNS. The method whose names it maps to
73        # should accept the same parameters as endElement and endElementNS.
74        end_dispatcher = {}
75    
76    
77      def __init__(self, directory):      def __init__(self, directory):
78          """Inititialize the Sax handler.          """Inititialize the Sax handler.
79    
80          directory is the directory containing the session file. It's          The directory parameter should be the directory containing the
81          needed to interpret embedded relative filenames          session file. It's needed to interpret embedded relative
82            filenames.
83          """          """
84          self.directory = directory          self.directory = directory
85          self.chars = ''          self.chars = ''
86    
87        def startElementNS(self, name, qname, attrs):
88            """Call the method given for name in self.start_dispatcher
89            """
90            if name[0] is None:
91                method_name = self.start_dispatcher.get(name[1])
92            else:
93                # Dispatch with namespace
94                method_name = self.start_dispatcher.get(name)
95            if method_name is not None:
96                getattr(self, method_name)(name, qname, attrs)
97    
98        def endElementNS(self, name, qname):
99            """Call the method given for name in self.end_dispatcher
100            """
101            if name[0] is None:
102                method_name = self.end_dispatcher.get(name[1])
103            else:
104                # Dispatch with namespace
105                method_name = self.end_dispatcher.get(name)
106            if method_name is not None:
107                getattr(self, method_name)(name, qname)
108    
109        def GetDirectory(self):
110            return self.directory
111    
112    class ProcessSession(XMLProcessor):
113    
114        def __init__(self, directory):
115            """Inititialize the Sax handler."""
116            XMLProcessor.__init__(self, directory)
117    
118          self.theSession = None          self.theSession = None
119          self.aMap = None          self.aMap = None
120          self.aLayer = None          self.aLayer = None
121    
122      def startElement(self, name, attrs):      def start_session(self, name, qname, attrs):
123          if name == 'session':          self.theSession = Session(attrs.get((None, 'title'), None))
124              self.theSession = Session(attrs.get('title', None))      XMLProcessor.start_dispatcher['session'] = "start_session"
125          elif name == 'map':  
126              self.aMap = Map(attrs.get('title', None))      def end_session(self, name, qname):
127          elif name == 'projection':          pass
128              self.ProjectionParams = [ ]      XMLProcessor.end_dispatcher['session'] = "end_session"
129          elif name == 'parameter':  
130              self.ProjectionParams.append(attrs.get('value', None))      def start_map(self, name, qname, attrs):
131          elif name == 'layer':          """Start a map."""
132              title = attrs.get('title', "")          self.aMap = Map(attrs.get((None, 'title'), None))
133              filename = attrs.get('filename', "")      XMLProcessor.start_dispatcher['map'] = "start_map"
134              filename = os.path.join(self.directory, filename)  
135              fill = parse_color(attrs.get('fill', "None"))      def end_map(self, name, qname):
136              stroke = parse_color(attrs.get('stroke', "#000000"))          self.theSession.AddMap(self.aMap)
137              stroke_width = int(attrs.get("stroke_width", "1"))      XMLProcessor.end_dispatcher['map'] = "end_map"
138              self.aLayer = Layer(title, filename, fill = fill, stroke = stroke,  
139                                  stroke_width = stroke_width)      def start_projection(self, name, qname, attrs):
140          elif name == 'table':          self.ProjectionParams = [ ]
141              print "table title: %s" % attrs.get('title', None)      XMLProcessor.start_dispatcher['projection'] = "start_projection"
142          elif name == 'labellayer':  
143              self.aLayer = self.aMap.LabelLayer()      def end_projection(self, name, qname):
144          elif name == 'label':          self.aMap.SetProjection(Projection(self.ProjectionParams))
145              x = float(attrs['x'])      XMLProcessor.end_dispatcher['projection'] = "end_projection"
146              y = float(attrs['y'])  
147              text = attrs['text']      def start_parameter(self, name, qname, attrs):
148              halign = attrs['halign']          s = attrs.get((None, 'value'))
149              valign = attrs['valign']          s = str(s) # we can't handle unicode in proj
150              self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)          self.ProjectionParams.append(s)
151                XMLProcessor.start_dispatcher['parameter'] = "start_parameter"
152    
153      if not oldPython and test_for_broken_SAX():      def start_layer(self, name, qname, attrs, layer_class = Layer):
154          # works with python 2.0, but is not SAX compliant          """Start a layer
155          def characters(self, ch):  
156              self.my_characters(ch)          Instantiate a layer of class layer_class from the attributes in
157      else:          attrs which may be a dictionary as well as the normal SAX attrs
158          # SAX compliant          object and bind it to self.aLayer.
159          def characters(self, ch, start, length):          """
160              self.my_characters(ch[start:start+length])          title = attrs.get((None, 'title'), "")
161            filename = attrs.get((None, 'filename'), "")
162      def my_characters(self, ch):          filename = os.path.join(self.GetDirectory(), filename)
163          self.chars = self.chars + ch          fill = parse_color(attrs.get((None, 'fill'), "None"))
164            stroke = parse_color(attrs.get((None, 'stroke'), "#000000"))
165      def endElement(self, name):          stroke_width = int(attrs.get((None, 'stroke_width'), "1"))
166          # If it's not a parameter element, ignore it          self.aLayer = layer_class(title, filename, fill = fill,
167          if name == 'session':                                    stroke = stroke, lineWidth = stroke_width)
168              #print "end of session"      XMLProcessor.start_dispatcher['layer'] = "start_layer"
169              pass  
170          if name == 'map':      def end_layer(self, name, qname):
171              self.theSession.AddMap(self.aMap)          self.aMap.AddLayer(self.aLayer)
172          if name == 'projection':      XMLProcessor.end_dispatcher['layer'] = "end_layer"
173              self.aMap.SetProjection(Projection(self.ProjectionParams))  
174          if name == 'layer':      def start_classification(self, name, qname, attrs):
175              self.aMap.AddLayer(self.aLayer)          field = attrs.get((None, 'field'), None)
176          if name == 'table':  
177              #print "end of table"          fieldType = attrs.get((None, 'field_type'), None)
178              pass          dbFieldType = self.aLayer.GetFieldType(field)
179    
180            if fieldType != dbFieldType:
181                raise ValueError(_("xml field type differs from database!"))
182    
183            # setup conversion routines depending on the kind of data
184            # we will be seeing later on
185            if fieldType == FIELDTYPE_STRING:
186                self.conv = str
187            elif fieldType == FIELDTYPE_INT:
188                self.conv = lambda p: int(float(p))
189            elif fieldType == FIELDTYPE_DOUBLE:
190                self.conv = float
191    
192            self.aLayer.GetClassification().SetField(field)
193    
194        XMLProcessor.start_dispatcher['classification'] = "start_classification"
195    
196        def end_classification(self, name, qname):
197            pass
198        XMLProcessor.end_dispatcher['classification'] = "end_classification"
199    
200        def start_clnull(self, name, qname, attrs):
201            self.cl_group = ClassGroupDefault()
202            self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
203            self.cl_prop = ClassGroupProperties()
204        XMLProcessor.start_dispatcher['clnull'] = "start_clnull"
205    
206        def end_clnull(self, name, qname):
207            self.cl_group.SetProperties(self.cl_prop)
208            self.aLayer.GetClassification().SetDefaultGroup(self.cl_group)
209            del self.cl_group, self.cl_prop
210        XMLProcessor.end_dispatcher['clnull'] = "end_clnull"
211    
212        def start_clpoint(self, name, qname, attrs):
213            attrib_value = attrs.get((None, 'value'), "0")
214    
215            #try:
216                #value  = Str2Num(attrib_value)
217            #except:
218                #value  = attrib_value
219    
220            value = self.conv(attrib_value)
221    
222            self.cl_group = ClassGroupSingleton(value)
223            self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
224            self.cl_prop = ClassGroupProperties()
225    
226        XMLProcessor.start_dispatcher['clpoint'] = "start_clpoint"
227    
228        def end_clpoint(self, name, qname):
229            self.cl_group.SetProperties(self.cl_prop)
230            self.aLayer.GetClassification().AppendGroup(self.cl_group)
231            del self.cl_group, self.cl_prop
232        XMLProcessor.end_dispatcher['clpoint'] = "end_clpoint"
233    
234        def start_clrange(self, name, qname, attrs):
235    
236            try:
237                min = self.conv(attrs.get((None, 'min'), "0"))
238                max = self.conv(attrs.get((None, 'max'), "0"))
239                #min = Str2Num(attrs.get((None, 'min'), "0"))
240                #max = Str2Num(attrs.get((None, 'max'), "0"))
241            except ValueError:
242                raise ValueError(_("Classification range is not a number!"))
243    
244            self.cl_group = ClassGroupRange(min, max)
245            self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
246            self.cl_prop = ClassGroupProperties()
247    
248        XMLProcessor.start_dispatcher['clrange'] = "start_clrange"
249    
250        def end_clrange(self, name, qname):
251            self.cl_group.SetProperties(self.cl_prop)
252            self.aLayer.GetClassification().AppendGroup(self.cl_group)
253            del self.cl_group, self.cl_prop
254        XMLProcessor.end_dispatcher['clrange'] = "end_clrange"
255    
256        def start_cldata(self, name, qname, attrs):
257            self.cl_prop.SetLineColor(
258                parse_color(attrs.get((None, 'stroke'), "None")))
259            self.cl_prop.SetLineWidth(
260                int(attrs.get((None, 'stroke_width'), "0")))
261            self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
262        XMLProcessor.start_dispatcher['cldata'] = "start_cldata"
263    
264        def end_cldata(self, name, qname):
265            pass
266        XMLProcessor.end_dispatcher['cldata'] = "end_cldata"
267    
268        def start_table(self, name, qname, attrs):
269            #print "table title: %s" % attrs.get('title', None)
270            pass
271        XMLProcessor.start_dispatcher['table'] = "start_table"
272    
273        def end_table(self, name, qname):
274            pass
275        XMLProcessor.end_dispatcher['table'] = "end_table"
276    
277        def start_labellayer(self, name, qname, attrs):
278            self.aLayer = self.aMap.LabelLayer()
279        XMLProcessor.start_dispatcher['labellayer'] = "start_labellayer"
280    
281        def start_label(self, name, qname, attrs):
282            x = float(attrs[(None, 'x')])
283            y = float(attrs[(None, 'y')])
284            text = attrs[(None, 'text')]
285            halign = attrs[(None, 'halign')]
286            valign = attrs[(None, 'valign')]
287            self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)
288        XMLProcessor.start_dispatcher['label'] = "start_label"
289    
290        def characters(self, chars):
291            pass
292    
293    
294    def load_xmlfile(filename, handler):
295        file = open(filename)
296    
297        parser = make_parser()
298        parser.setContentHandler(handler)
299        parser.setErrorHandler(ErrorHandler())
300        parser.setFeature(xml.sax.handler.feature_namespaces, 1)
301    
302        #
303        # Well, this isn't pretty, but it appears that if you
304        # use Python 2.2 without the site-package _xmlplus then
305        # the following will fail, and without them it will work.
306        # However, if you do have the site-package and you don't
307        # call these functions, the reader raises an exception
308        #
309        # The reason we set these to 0 in the first place is
310        # because there is an unresolved issue with external
311        # entities causing an exception in the reader
312        #
313        try:
314            parser.setFeature(xml.sax.handler.feature_validation, 0)
315            parser.setFeature(xml.sax.handler.feature_external_ges, 0)
316            parser.setFeature(xml.sax.handler.feature_external_pes, 0)
317        except SAXNotRecognizedException:
318            pass
319    
320        parser.parse(file)
321    
322  def load_session(filename):  def load_session(filename):
323      """Load a Thuban session from the file object file"""      """Load a Thuban session from the file object file"""
324    
325      dir = os.path.dirname(filename)      dir = os.path.dirname(filename)
     file = open(filename)  
326      handler = ProcessSession(dir)      handler = ProcessSession(dir)
327    
328      if oldPython:      load_xmlfile(filename, handler)
329          parser = make_parser()  
         parser.setDocumentHandler(handler)  
         parser.setErrorHandler(saxutils.ErrorPrinter())  
         parser.parseFile(file)  
         parser.close()  
     else:  
         xml.sax.parse(file,handler)  
330      session = handler.theSession      session = handler.theSession
331      # Newly loaded session aren't modified      # Newly loaded session aren't modified
332      session.UnsetModified()      session.UnsetModified()
333    
334      return session      return session
335    
 if __name__ == "__main__":  
     # find out the command to run  
     if len(sys.argv) > 1:  
         print "usage: cat <file> | " + sys.argv[0]  
     else:  
         parseSession(sys.stdin)  

Legend:
Removed from v.75  
changed lines
  Added in v.694

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26