/[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 1268 by bh, Fri Jun 20 16:10:12 2003 UTC revision 2446 by frank, Mon Dec 13 11:52:34 2004 UTC
# Line 1  Line 1 
1  # Copyright (C) 2001, 2002, 2003 by Intevation GmbH  # Copyright (C) 2001, 2002, 2003, 2004 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]>  # Jonathan Coles <[email protected]>
6    # Frank Koormann <[email protected]>
7  #  #
8  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
9  # Read the file COPYING coming with GRASS for details.  # Read the file COPYING coming with GRASS for details.
# Line 24  from Thuban import _ Line 25  from Thuban import _
25  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
26       FIELDTYPE_STRING       FIELDTYPE_STRING
27    
28    from Thuban.Model.color import Color, Transparent
29    
30  from Thuban.Model.session import Session  from Thuban.Model.session import Session
31  from Thuban.Model.map import Map  from Thuban.Model.map import Map
32  from Thuban.Model.layer import Layer, RasterLayer  from Thuban.Model.layer import Layer, RasterLayer
 from Thuban.Model.color import Color  
33  from Thuban.Model.proj import Projection  from Thuban.Model.proj import Projection
34  from Thuban.Model.range import Range  from Thuban.Model.range import Range
35  from Thuban.Model.classification import Classification, \  from Thuban.Model.classification import Classification, \
# Line 37  from Thuban.Model.data import DerivedSha Line 39  from Thuban.Model.data import DerivedSha
39  from Thuban.Model.table import DBFTable  from Thuban.Model.table import DBFTable
40  from Thuban.Model.transientdb import TransientJoinedTable  from Thuban.Model.transientdb import TransientJoinedTable
41    
42    from Thuban.Model.xmlreader import XMLReader
43    import resource
44    
45    import postgisdb
46    
47  class LoadError(Exception):  class LoadError(Exception):
     pass  
48    
49  from Thuban.Model.xmlreader import XMLReader      """Exception raised when the thuban file is corrupted
50  import resource  
51        Not all cases of corrupted thuban files will lead to this exception
52        but those that are found by checks in the loading code itself are.
53        """
54    
55    
56    class LoadCancelled(Exception):
57    
58        """Exception raised to indicate that loading was interrupted by the user"""
59    
60    
61  def parse_color(color):  def parse_color(color):
62      """Return the color object for the string color.      """Return the color object for the string color.
# Line 51  def parse_color(color): Line 66  def parse_color(color):
66      """      """
67      color = string.strip(color)      color = string.strip(color)
68      if color == "None":      if color == "None":
69          result = Color.Transparent          result = Transparent
70      elif color[0] == '#':      elif color[0] == '#':
71          if len(color) == 7:          if len(color) == 7:
72              r = string.atoi(color[1:3], 16) / 255.0              r = string.atoi(color[1:3], 16) / 255.0
# Line 86  class AttrDesc: Line 101  class AttrDesc:
101    
102  class SessionLoader(XMLReader):  class SessionLoader(XMLReader):
103    
104      def __init__(self):      def __init__(self, db_connection_callback = None,
105                           shapefile_callback = None):
106          """Inititialize the Sax handler."""          """Inititialize the Sax handler."""
107          XMLReader.__init__(self)          XMLReader.__init__(self)
108    
109            self.db_connection_callback = db_connection_callback
110            self.shapefile_callback = shapefile_callback
111          self.theSession = None          self.theSession = None
112          self.aMap = None          self.aMap = None
113          self.aLayer = None          self.aLayer = None
# Line 100  class SessionLoader(XMLReader): Line 118  class SessionLoader(XMLReader):
118    
119          dispatchers = {          dispatchers = {
120              'session'       : ("start_session",        "end_session"),              'session'       : ("start_session",        "end_session"),
121    
122                'dbconnection': ("start_dbconnection", None),
123    
124                'dbshapesource': ("start_dbshapesource", None),
125              'fileshapesource': ("start_fileshapesource", None),              'fileshapesource': ("start_fileshapesource", None),
126              'derivedshapesource': ("start_derivedshapesource", None),              'derivedshapesource': ("start_derivedshapesource", None),
127              'filetable': ("start_filetable", None),              'filetable': ("start_filetable", None),
# Line 119  class SessionLoader(XMLReader): Line 141  class SessionLoader(XMLReader):
141              'labellayer'    : ("start_labellayer",     None),              'labellayer'    : ("start_labellayer",     None),
142              'label'         : ("start_label",          None)}              'label'         : ("start_label",          None)}
143    
144          # all dispatchers should be used for the 0.8 namespace          # all dispatchers should be used for the 0.8 and 0.9 namespaces too
145          xmlns = "http://thuban.intevation.org/dtds/thuban-0.8.dtd"          for xmlns in ("http://thuban.intevation.org/dtds/thuban-0.8.dtd",
146          for key, value in dispatchers.items():                        "http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd",
147              dispatchers[(xmlns, key)] = value                        "http://thuban.intevation.org/dtds/thuban-0.9.dtd",
148                          "http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd",
149                          "http://thuban.intevation.org/dtds/thuban-1.0rc1.dtd",
150                          "http://thuban.intevation.org/dtds/thuban-1.0.0.dtd",
151                          "http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"):
152                for key, value in dispatchers.items():
153                    dispatchers[(xmlns, key)] = value
154    
155          XMLReader.AddDispatchers(self, dispatchers)          XMLReader.AddDispatchers(self, dispatchers)
156    
157        def Destroy(self):
158            """Clear all instance variables to cut cyclic references.
159    
160            The GC would have collected the loader eventually but it can
161            happen that it doesn't run at all until Thuban is closed (2.3
162            but not 2.2 tries a bit harder and forces a collection when the
163            interpreter terminates)
164            """
165            self.__dict__.clear()
166    
167      def start_session(self, name, qname, attrs):      def start_session(self, name, qname, attrs):
168          self.theSession = Session(self.encode(attrs.get((None, 'title'),          self.theSession = Session(self.encode(attrs.get((None, 'title'),
169                                                          None)))                                                          None)))
# Line 153  class SessionLoader(XMLReader): Line 191  class SessionLoader(XMLReader):
191          If the attribute has a default value and it is not present in          If the attribute has a default value and it is not present in
192          attrs, use that default value as the value in the returned dict.          attrs, use that default value as the value in the returned dict.
193    
194          If a conversion is specified, convert the value before putting          The value is converted before putting it into the returned dict.
195          it into the returned dict. The following conversions are          The following conversions are available:
         available:  
196    
197             'filename' -- The attribute is a filename.             'filename' -- The attribute is a filename.
198    
# Line 171  class SessionLoader(XMLReader): Line 208  class SessionLoader(XMLReader):
208                        defined earlier in the .thuban file. Look it up                        defined earlier in the .thuban file. Look it up
209                        self.idmap. If it's the ID of a shapestore the                        self.idmap. If it's the ID of a shapestore the
210                        value will be the table of the shapestore.                        value will be the table of the shapestore.
211    
212               'idref' -- The attribute is the id of an object defined
213                          earlier in the .thuban file. Look it up self.idmap
214    
215               'ascii' -- The attribute is converted to a bytestring with
216                          ascii encoding.
217    
218               a callable -- The attribute value is passed to the callable
219                             and the return value is used as the converted
220                             value
221    
222            If no conversion is specified for an attribute it is converted
223            with self.encode.
224          """          """
225          normalized = {}          normalized = {}
226    
227          for d in descr:          for d in descr:
228              if d.required and not attrs.has_key(d.fullname):              if d.required and not attrs.has_key(d.fullname):
229                  pass                  raise LoadError("Element %s requires an attribute %r"
230              #raise LoadError("Element %s requires an attribute %r"                                  % (element, d.name))
             #                    % (element, d.name))  
231              value = attrs.get(d.fullname, d.default)              value = attrs.get(d.fullname, d.default)
232    
233              if d.conversion == "shapesource":              if d.conversion in ("idref", "shapesource"):
234                  if value in self.idmap:                  if value in self.idmap:
235                      value = self.idmap[value]                      value = self.idmap[value]
236                  else:                  else:
# Line 200  class SessionLoader(XMLReader): Line 249  class SessionLoader(XMLReader):
249              elif d.conversion == "filename":              elif d.conversion == "filename":
250                  value = os.path.abspath(os.path.join(self.GetDirectory(),                  value = os.path.abspath(os.path.join(self.GetDirectory(),
251                                                       value))                                                       value))
252                elif d.conversion == "ascii":
253                    value = value.encode("ascii")
254                elif d.conversion:
255                    # Assume it's a callable
256                    value = d.conversion(value)
257                else:
258                   value = self.encode(value)
259    
260              normalized[d.name] = value              normalized[d.name] = value
261          return normalized          return normalized
262    
263        def open_shapefile(self, filename):
264            """Open shapefile, eventually with alternative path."""
265            from_list = 0
266            while 1:
267                try:
268                    store = self.theSession.OpenShapefile(filename)
269                    if from_list:
270                        # The correct? path has been guessed from a list
271                        # Let the user confirm - or select an alternative.
272                        filename, from_list = self.shapefile_callback(
273                                                filename, "check")
274                        if filename is None:
275                            # Selection cancelled
276                            raise LoadCancelled
277                        elif store.FileName() == filename:
278                            # Proposed file has been accepted
279                            break
280                        else:
281                            # the filename has been changed, try the new file
282                            pass
283                    else:
284                        break
285                except IOError:
286                    if self.shapefile_callback is not None:
287                        filename, from_list = self.shapefile_callback(
288                                                filename,
289                                                mode = "search",
290                                                second_try = from_list)
291                        if filename is None:
292                            raise LoadCancelled
293                        print filename
294                    else:
295                        raise
296            return store
297    
298        def start_dbconnection(self, name, qname, attrs):
299            attrs = self.check_attrs(name, attrs,
300                                     [AttrDesc("id", True),
301                                      AttrDesc("dbtype", True),
302                                      AttrDesc("host", False, ""),
303                                      AttrDesc("port", False, ""),
304                                      AttrDesc("user", False, ""),
305                                      AttrDesc("dbname", True)])
306            ID = attrs["id"]
307            dbtype = attrs["dbtype"]
308            if dbtype != "postgis":
309                raise LoadError("dbtype %r not supported" % filetype)
310    
311            del attrs["id"]
312            del attrs["dbtype"]
313    
314            # Try to open the connection and if it fails ask the user for
315            # the correct parameters repeatedly.
316            # FIXME: it would be better not to insist on getting a
317            # connection here. We should handle this more like the raster
318            # images where the layers etc still are created but are not
319            # drawn in case Thuban can't use the data for various reasons
320            while 1:
321                try:
322                    conn = postgisdb.PostGISConnection(**attrs)
323                    break
324                except postgisdb.ConnectionError, val:
325                    if self.db_connection_callback is not None:
326                        attrs = self.db_connection_callback(attrs, str(val))
327                        if attrs is None:
328                            raise LoadCancelled
329                    else:
330                        raise
331    
332            self.idmap[ID] = conn
333            self.theSession.AddDBConnection(conn)
334    
335        def start_dbshapesource(self, name, qname, attrs):
336            attrs = self.check_attrs(name, attrs,
337                                     [AttrDesc("id", True),
338                                      AttrDesc("dbconn", True,
339                                               conversion = "idref"),
340                                      AttrDesc("tablename", True,
341                                               conversion = "ascii"),
342                                      # id_column and geometry_column were
343                                      # newly introduced with thuban-1.1.dtd
344                                      # where they're required.  Since we
345                                      # support the older formats too we
346                                      # have them optional here.
347                                      AttrDesc("id_column", False, "gid",
348                                               conversion = "ascii"),
349                                      AttrDesc("geometry_column", False,
350                                               conversion = "ascii")])
351            # The default value of geometry_column to use when instantiating
352            # the db shapestore is None which we currently can't easily use
353            # in check_attrs
354            geometry_column = attrs["geometry_column"]
355            if not geometry_column:
356                geometry_column = None
357            dbopen = self.theSession.OpenDBShapeStore
358            self.idmap[attrs["id"]] = dbopen(attrs["dbconn"], attrs["tablename"],
359                                             id_column = attrs["id_column"],
360                                             geometry_column=geometry_column)
361    
362      def start_fileshapesource(self, name, qname, attrs):      def start_fileshapesource(self, name, qname, attrs):
363          attrs = self.check_attrs(name, attrs,          attrs = self.check_attrs(name, attrs,
364                                    [AttrDesc("id", True),                                    [AttrDesc("id", True),
# Line 215  class SessionLoader(XMLReader): Line 370  class SessionLoader(XMLReader):
370          filetype = attrs["filetype"]          filetype = attrs["filetype"]
371          if filetype != "shapefile":          if filetype != "shapefile":
372              raise LoadError("shapesource filetype %r not supported" % filetype)              raise LoadError("shapesource filetype %r not supported" % filetype)
373          self.idmap[ID] = self.theSession.OpenShapefile(filename)          self.idmap[ID] = self.open_shapefile(filename)
374    
375      def start_derivedshapesource(self, name, qname, attrs):      def start_derivedshapesource(self, name, qname, attrs):
376          attrs = self.check_attrs(name, attrs,          attrs = self.check_attrs(name, attrs,
# Line 223  class SessionLoader(XMLReader): Line 378  class SessionLoader(XMLReader):
378                                    AttrDesc("shapesource", True,                                    AttrDesc("shapesource", True,
379                                             conversion = "shapesource"),                                             conversion = "shapesource"),
380                                    AttrDesc("table", True, conversion="table")])                                    AttrDesc("table", True, conversion="table")])
381          self.idmap[attrs["id"]] = DerivedShapeStore(attrs["shapesource"],          store = DerivedShapeStore(attrs["shapesource"], attrs["table"])
382                                                      attrs["table"])          self.theSession.AddShapeStore(store)
383            self.idmap[attrs["id"]] = store
384    
385      def start_filetable(self, name, qname, attrs):      def start_filetable(self, name, qname, attrs):
386          attrs = self.check_attrs(name, attrs,          attrs = self.check_attrs(name, attrs,
# Line 247  class SessionLoader(XMLReader): Line 403  class SessionLoader(XMLReader):
403                                    AttrDesc("left", True, conversion="table"),                                    AttrDesc("left", True, conversion="table"),
404                                    AttrDesc("leftcolumn", True),                                    AttrDesc("leftcolumn", True),
405                                    AttrDesc("right", True, conversion="table"),                                    AttrDesc("right", True, conversion="table"),
406                                    AttrDesc("rightcolumn")])                                    AttrDesc("rightcolumn", True),
407    
408                                      # jointype is required for file
409                                      # version 0.9 but this attribute
410                                      # wasn't in the 0.8 version because of
411                                      # an oversight so we assume it's
412                                      # optional since we want to handle
413                                      # both file format versions here.
414                                      AttrDesc("jointype", False,
415                                               default="INNER")])
416    
417            jointype = attrs["jointype"]
418            if jointype == "LEFT OUTER":
419                outer_join = True
420            elif jointype == "INNER":
421                outer_join = False
422            else:
423                raise LoadError("jointype %r not supported" % jointype )
424          table = TransientJoinedTable(self.theSession.TransientDB(),          table = TransientJoinedTable(self.theSession.TransientDB(),
425                                       attrs["left"], attrs["leftcolumn"],                                       attrs["left"], attrs["leftcolumn"],
426                                       attrs["right"], attrs["rightcolumn"])                                       attrs["right"], attrs["rightcolumn"],
427                                         outer_join = outer_join)
428          table.SetTitle(attrs["title"])          table.SetTitle(attrs["title"])
429          self.idmap[attrs["id"]] = self.theSession.AddTable(table)          self.idmap[attrs["id"]] = self.theSession.AddTable(table)
430    
431      def start_map(self, name, qname, attrs):      def start_map(self, name, qname, attrs):
432          """Start a map."""          """Start a map."""
433          self.aMap = Map(attrs.get((None, 'title'), None))          self.aMap = Map(self.encode(attrs.get((None, 'title'), None)))
434    
435      def end_map(self, name, qname):      def end_map(self, name, qname):
436          self.theSession.AddMap(self.aMap)          self.theSession.AddMap(self.aMap)
437          self.aMap = None          self.aMap = None
438    
439      def start_projection(self, name, qname, attrs):      def start_projection(self, name, qname, attrs):
440          self.ProjectionName = self.encode(attrs.get((None, 'name'), None))          attrs = self.check_attrs(name, attrs,
441          self.ProjectionParams = [ ]                                   [AttrDesc("name", conversion=self.encode),
442                                      AttrDesc("epsg", default=None,
443                                               conversion=self.encode)])
444            self.projection_name = attrs["name"]
445            self.projection_epsg = attrs["epsg"]
446            self.projection_params = [ ]
447    
448      def end_projection(self, name, qname):      def end_projection(self, name, qname):
449          if self.aLayer is not None:          if self.aLayer is not None:
# Line 275  class SessionLoader(XMLReader): Line 454  class SessionLoader(XMLReader):
454              assert False, "projection tag out of context"              assert False, "projection tag out of context"
455              pass              pass
456    
457          obj.SetProjection(          obj.SetProjection(Projection(self.projection_params,
458              Projection(self.ProjectionParams, self.ProjectionName))                                       self.projection_name,
459                                         epsg = self.projection_epsg))
460    
461      def start_parameter(self, name, qname, attrs):      def start_parameter(self, name, qname, attrs):
462          s = attrs.get((None, 'value'))          s = attrs.get((None, 'value'))
463          s = str(s) # we can't handle unicode in proj          s = str(s) # we can't handle unicode in proj
464          self.ProjectionParams.append(s)          self.projection_params.append(s)
465    
466      def start_layer(self, name, qname, attrs, layer_class = Layer):      def start_layer(self, name, qname, attrs, layer_class = Layer):
467          """Start a layer          """Start a layer
# Line 301  class SessionLoader(XMLReader): Line 481  class SessionLoader(XMLReader):
481          if attrs.has_key((None, "shapestore")):          if attrs.has_key((None, "shapestore")):
482              store = self.idmap[attrs[(None, "shapestore")]]              store = self.idmap[attrs[(None, "shapestore")]]
483          else:          else:
484              store = self.theSession.OpenShapefile(filename)              store = self.open_shapefile(filename)
485    
486          self.aLayer = layer_class(title, store,          self.aLayer = layer_class(title, store,
487                                    fill = fill, stroke = stroke,                                    fill = fill, stroke = stroke,
488                                    lineWidth = stroke_width,                                    lineWidth = stroke_width,
# Line 325  class SessionLoader(XMLReader): Line 506  class SessionLoader(XMLReader):
506          self.aLayer = None          self.aLayer = None
507    
508      def start_classification(self, name, qname, attrs):      def start_classification(self, name, qname, attrs):
509          field = attrs.get((None, 'field'), None)          attrs = self.check_attrs(name, attrs,
510                                     [AttrDesc("field", True),
511                                      AttrDesc("field_type", True)])
512            field = attrs["field"]
513            fieldType = attrs["field_type"]
514    
         fieldType = attrs.get((None, 'field_type'), None)  
515          dbFieldType = self.aLayer.GetFieldType(field)          dbFieldType = self.aLayer.GetFieldType(field)
516    
517          if fieldType != dbFieldType:          if fieldType != dbFieldType:
# Line 342  class SessionLoader(XMLReader): Line 526  class SessionLoader(XMLReader):
526          elif fieldType == FIELDTYPE_DOUBLE:          elif fieldType == FIELDTYPE_DOUBLE:
527              self.conv = float              self.conv = float
528    
529          self.aLayer.GetClassification().SetField(field)          self.aLayer.SetClassificationColumn(field)
530    
531      def end_classification(self, name, qname):      def end_classification(self, name, qname):
532          pass          pass
# Line 360  class SessionLoader(XMLReader): Line 544  class SessionLoader(XMLReader):
544      def start_clpoint(self, name, qname, attrs):      def start_clpoint(self, name, qname, attrs):
545          attrib_value = attrs.get((None, 'value'), "0")          attrib_value = attrs.get((None, 'value'), "0")
546    
547          value = self.conv(attrib_value)          field = self.aLayer.GetClassificationColumn()
548            if self.aLayer.GetFieldType(field) == FIELDTYPE_STRING:
549                value = self.encode(attrib_value)
550            else:
551                value = self.conv(attrib_value)
552          self.cl_group = ClassGroupSingleton(value)          self.cl_group = ClassGroupSingleton(value)
553          self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))          self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
554          self.cl_prop = ClassGroupProperties()          self.cl_prop = ClassGroupProperties()
# Line 383  class SessionLoader(XMLReader): Line 570  class SessionLoader(XMLReader):
570              if range is not None:              if range is not None:
571                  self.cl_group = ClassGroupRange(Range(range))                  self.cl_group = ClassGroupRange(Range(range))
572              elif min is not None and max is not None:              elif min is not None and max is not None:
573                  self.cl_group = ClassGroupRange(self.conv(min), self.conv(max))                  self.cl_group = ClassGroupRange((self.conv(min),
574                                                     self.conv(max)))
575              else:              else:
576                  self.cl_group = ClassGroupRange(Range(None))                  self.cl_group = ClassGroupRange(Range(None))
577    
# Line 404  class SessionLoader(XMLReader): Line 592  class SessionLoader(XMLReader):
592              parse_color(attrs.get((None, 'stroke'), "None")))              parse_color(attrs.get((None, 'stroke'), "None")))
593          self.cl_prop.SetLineWidth(          self.cl_prop.SetLineWidth(
594              int(attrs.get((None, 'stroke_width'), "0")))              int(attrs.get((None, 'stroke_width'), "0")))
595            self.cl_prop.SetSize(int(attrs.get((None, 'size'), "5")))
596          self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))          self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
597    
598      def end_cldata(self, name, qname):      def end_cldata(self, name, qname):
# Line 413  class SessionLoader(XMLReader): Line 602  class SessionLoader(XMLReader):
602          self.aLayer = self.aMap.LabelLayer()          self.aLayer = self.aMap.LabelLayer()
603    
604      def start_label(self, name, qname, attrs):      def start_label(self, name, qname, attrs):
605          x = float(attrs[(None, 'x')])          attrs = self.check_attrs(name, attrs,
606          y = float(attrs[(None, 'y')])                                   [AttrDesc("x", True, conversion = float),
607          text = self.encode(attrs[(None, 'text')])                                    AttrDesc("y", True, conversion = float),
608          halign = attrs[(None, 'halign')]                                    AttrDesc("text", True),
609          valign = attrs[(None, 'valign')]                                    AttrDesc("halign", True,
610                                               conversion = "ascii"),
611                                      AttrDesc("valign", True,
612                                               conversion = "ascii")])
613            x = attrs['x']
614            y = attrs['y']
615            text = attrs['text']
616            halign = attrs['halign']
617            valign = attrs['valign']
618            if halign not in ("left", "center", "right"):
619                raise LoadError("Unsupported halign value %r" % halign)
620            if valign not in ("top", "center", "bottom"):
621                raise LoadError("Unsupported valign value %r" % valign)
622          self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)          self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)
623    
624      def characters(self, chars):      def characters(self, chars):
625          pass          pass
626    
627    
628  def load_session(filename):  def load_session(filename, db_connection_callback = None,
629      """Load a Thuban session from the file object file"""                             shapefile_callback = None):
630        """Load a Thuban session from the file object file
631      handler = SessionLoader()  
632        The db_connection_callback, if given should be a callable object
633        that can be called like this:
634           db_connection_callback(params, message)
635    
636        where params is a dictionary containing the known connection
637        parameters and message is a string with a message why the connection
638        failed. db_connection_callback should return a new dictionary with
639        corrected and perhaps additional parameters like a password or None
640        to indicate that the user cancelled.
641        """
642        handler = SessionLoader(db_connection_callback, shapefile_callback)
643      handler.read(filename)      handler.read(filename)
644    
645      session = handler.theSession      session = handler.theSession
646      # Newly loaded session aren't modified      # Newly loaded session aren't modified
647      session.UnsetModified()      session.UnsetModified()
648    
649        handler.Destroy()
650    
651      return session      return session
652    

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26