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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2449 - (hide annotations)
Mon Dec 13 13:37:40 2004 UTC (20 years, 2 months ago) by frank
Original Path: trunk/thuban/Thuban/Model/load.py
File MIME type: text/x-python
File size: 27434 byte(s)
Updated docstring for SessionLoader.open_shapefile

1 bh 2104 # Copyright (C) 2001, 2002, 2003, 2004 by Intevation GmbH
2 bh 6 # Authors:
3     # Jan-Oliver Wagner <[email protected]>
4 bh 267 # Bernhard Herzog <[email protected]>
5 jonathan 413 # Jonathan Coles <[email protected]>
6 frank 2446 # Frank Koormann <[email protected]>
7 bh 6 #
8     # This program is free software under the GPL (>=v2)
9     # Read the file COPYING coming with GRASS for details.
10    
11     """
12     Parser for thuban session files.
13     """
14    
15     __version__ = "$Revision$"
16    
17 bh 723 import string, os
18 bh 267
19     import xml.sax
20     import xml.sax.handler
21 jonathan 526 from xml.sax import make_parser, ErrorHandler, SAXNotRecognizedException
22 bh 267
23 jan 374 from Thuban import _
24 jonathan 413
25 jonathan 473 from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
26     FIELDTYPE_STRING
27    
28 jonathan 1339 from Thuban.Model.color import Color, Transparent
29    
30 bh 6 from Thuban.Model.session import Session
31     from Thuban.Model.map import Map
32 jonathan 930 from Thuban.Model.layer import Layer, RasterLayer
33 bh 6 from Thuban.Model.proj import Projection
34 jonathan 874 from Thuban.Model.range import Range
35 jonathan 413 from Thuban.Model.classification import Classification, \
36 jonathan 439 ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
37     ClassGroupProperties
38 bh 1268 from Thuban.Model.data import DerivedShapeStore, ShapefileStore
39     from Thuban.Model.table import DBFTable
40     from Thuban.Model.transientdb import TransientJoinedTable
41 bh 6
42 bh 1646 from Thuban.Model.xmlreader import XMLReader
43     import resource
44    
45     import postgisdb
46    
47 bh 1268 class LoadError(Exception):
48    
49 bh 1646 """Exception raised when the thuban file is corrupted
50 bh 6
51 bh 1646 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 bh 267 def parse_color(color):
62     """Return the color object for the string color.
63 bh 6
64 bh 267 Color may be either 'None' or of the form '#RRGGBB' in the usual
65     HTML color notation
66 bh 6 """
67     color = string.strip(color)
68     if color == "None":
69 jonathan 1339 result = Transparent
70 bh 6 elif color[0] == '#':
71     if len(color) == 7:
72     r = string.atoi(color[1:3], 16) / 255.0
73     g = string.atoi(color[3:5], 16) / 255.0
74     b = string.atoi(color[5:7], 16) / 255.0
75     result = Color(r, g, b)
76     else:
77 jan 374 raise ValueError(_("Invalid hexadecimal color specification %s")
78 bh 6 % color)
79     else:
80 jan 374 raise ValueError(_("Invalid color specification %s") % color)
81 bh 6 return result
82    
83 bh 1268 class AttrDesc:
84    
85     def __init__(self, name, required = False, default = "",
86     conversion = None):
87     if not isinstance(name, tuple):
88     fullname = (None, name)
89     else:
90     fullname = name
91     name = name[1]
92     self.name = name
93     self.fullname = fullname
94     self.required = required
95     self.default = default
96     self.conversion = conversion
97    
98     # set by the SessionLoader's check_attrs method
99     self.value = None
100    
101    
102 jonathan 706 class SessionLoader(XMLReader):
103 jonathan 694
104 frank 2446 def __init__(self, db_connection_callback = None,
105     shapefile_callback = None):
106 jonathan 694 """Inititialize the Sax handler."""
107 jonathan 706 XMLReader.__init__(self)
108 jonathan 694
109 bh 1646 self.db_connection_callback = db_connection_callback
110 frank 2446 self.shapefile_callback = shapefile_callback
111 jonathan 694 self.theSession = None
112     self.aMap = None
113     self.aLayer = None
114    
115 bh 1268 # Map ids used in the thuban file to the corresponding objects
116     # in the session
117     self.idmap = {}
118 jonathan 706
119 bh 1268 dispatchers = {
120     'session' : ("start_session", "end_session"),
121 bh 1646
122     'dbconnection': ("start_dbconnection", None),
123    
124     'dbshapesource': ("start_dbshapesource", None),
125 bh 1268 'fileshapesource': ("start_fileshapesource", None),
126     'derivedshapesource': ("start_derivedshapesource", None),
127     'filetable': ("start_filetable", None),
128     'jointable': ("start_jointable", None),
129    
130     'map' : ("start_map", "end_map"),
131     'projection' : ("start_projection", "end_projection"),
132     'parameter' : ("start_parameter", None),
133     'layer' : ("start_layer", "end_layer"),
134     'rasterlayer' : ("start_rasterlayer", "end_rasterlayer"),
135     'classification': ("start_classification", "end_classification"),
136     'clnull' : ("start_clnull", "end_clnull"),
137     'clpoint' : ("start_clpoint", "end_clpoint"),
138     'clrange' : ("start_clrange", "end_clrange"),
139     'cldata' : ("start_cldata", "end_cldata"),
140     'table' : ("start_table", "end_table"),
141     'labellayer' : ("start_labellayer", None),
142     'label' : ("start_label", None)}
143    
144 bh 1375 # all dispatchers should be used for the 0.8 and 0.9 namespaces too
145     for xmlns in ("http://thuban.intevation.org/dtds/thuban-0.8.dtd",
146 bh 1664 "http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd",
147 bh 1844 "http://thuban.intevation.org/dtds/thuban-0.9.dtd",
148 bh 2004 "http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd",
149 bh 2036 "http://thuban.intevation.org/dtds/thuban-1.0rc1.dtd",
150 bh 2104 "http://thuban.intevation.org/dtds/thuban-1.0.0.dtd",
151     "http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"):
152 bh 1375 for key, value in dispatchers.items():
153     dispatchers[(xmlns, key)] = value
154 bh 1268
155     XMLReader.AddDispatchers(self, dispatchers)
156    
157 bh 1930 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 bh 267 def start_session(self, name, qname, attrs):
168 bh 1268 self.theSession = Session(self.encode(attrs.get((None, 'title'),
169     None)))
170 bh 6
171 bh 267 def end_session(self, name, qname):
172     pass
173 bh 6
174 bh 1268 def check_attrs(self, element, attrs, descr):
175     """Check and convert some of the attributes of an element
176    
177     Parameters:
178     element -- The element name
179     attrs -- The attrs mapping as passed to the start_* methods
180     descr -- Sequence of attribute descriptions (AttrDesc instances)
181    
182     Return a dictionary containig normalized versions of the
183     attributes described in descr. The keys of that dictionary are
184     the name attributes of the attribute descriptions. The attrs
185     dictionary will not be modified.
186    
187     If the attribute is required, i.e. the 'required' attribute of
188     the descrtiption is true, but it is not in attrs, raise a
189     LoadError.
190    
191     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.
193    
194 bh 1970 The value is converted before putting it into the returned dict.
195     The following conversions are available:
196 bh 1268
197     'filename' -- The attribute is a filename.
198    
199     If the filename is a relative name, interpret
200     it relative to the directory containing the
201     .thuban file and make it an absolute name
202    
203     'shapestore' -- The attribute is the ID of a shapestore
204     defined earlier in the .thuban file. Look it
205     up self.idmap
206    
207     'table' -- The attribute is the ID of a table or shapestore
208     defined earlier in the .thuban file. Look it up
209     self.idmap. If it's the ID of a shapestore the
210     value will be the table of the shapestore.
211 bh 1646
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 bh 1844
218     a callable -- The attribute value is passed to the callable
219 bh 1970 and the return value is used as the converted
220 bh 1844 value
221 bh 1970
222     If no conversion is specified for an attribute it is converted
223     with self.encode.
224 bh 1268 """
225     normalized = {}
226    
227     for d in descr:
228     if d.required and not attrs.has_key(d.fullname):
229 bh 1642 raise LoadError("Element %s requires an attribute %r"
230     % (element, d.name))
231 bh 1268 value = attrs.get(d.fullname, d.default)
232    
233 bh 1646 if d.conversion in ("idref", "shapesource"):
234 bh 1268 if value in self.idmap:
235     value = self.idmap[value]
236     else:
237     raise LoadError("Element %s requires an already defined ID"
238     " in attribute %r"
239     % (element, d.name))
240     elif d.conversion == "table":
241     if value in self.idmap:
242     value = self.idmap[value]
243     if isinstance(value, ShapefileStore):
244     value = value.Table()
245     else:
246     raise LoadError("Element %s requires an already defined ID"
247     " in attribute %r"
248     % (element, d.name))
249     elif d.conversion == "filename":
250     value = os.path.abspath(os.path.join(self.GetDirectory(),
251     value))
252 bh 1646 elif d.conversion == "ascii":
253     value = value.encode("ascii")
254 bh 1844 elif d.conversion:
255     # Assume it's a callable
256     value = d.conversion(value)
257 bh 1970 else:
258     value = self.encode(value)
259 bh 1268
260     normalized[d.name] = value
261     return normalized
262    
263 frank 2446 def open_shapefile(self, filename):
264 frank 2449 """Open shapefile, with alternative path handling.
265    
266     If a shapefile cannot be opened and an IOError is raised, check for
267     an alternative. This alternative can be specified interactively by
268     the user or taken from a list of (potential) locations, depending on
269     the callback implementation.
270    
271     The alternative is rechecked. If taken from a list the user
272     has to confirm the alternative.
273     """
274    
275     # Flag if the alternative path was specified interactively / from list.
276 frank 2446 from_list = 0
277     while 1:
278     try:
279     store = self.theSession.OpenShapefile(filename)
280     if from_list:
281 frank 2449 # A valid path has been guessed from a list
282 frank 2446 # Let the user confirm - or select an alternative.
283     filename, from_list = self.shapefile_callback(
284     filename, "check")
285     if filename is None:
286     # Selection cancelled
287     raise LoadCancelled
288     elif store.FileName() == filename:
289     # Proposed file has been accepted
290     break
291     else:
292     # the filename has been changed, try the new file
293     pass
294     else:
295     break
296     except IOError:
297     if self.shapefile_callback is not None:
298     filename, from_list = self.shapefile_callback(
299     filename,
300     mode = "search",
301     second_try = from_list)
302     if filename is None:
303     raise LoadCancelled
304     print filename
305     else:
306     raise
307     return store
308    
309 bh 1646 def start_dbconnection(self, name, qname, attrs):
310     attrs = self.check_attrs(name, attrs,
311     [AttrDesc("id", True),
312     AttrDesc("dbtype", True),
313     AttrDesc("host", False, ""),
314     AttrDesc("port", False, ""),
315     AttrDesc("user", False, ""),
316     AttrDesc("dbname", True)])
317     ID = attrs["id"]
318     dbtype = attrs["dbtype"]
319     if dbtype != "postgis":
320     raise LoadError("dbtype %r not supported" % filetype)
321    
322     del attrs["id"]
323     del attrs["dbtype"]
324    
325     # Try to open the connection and if it fails ask the user for
326     # the correct parameters repeatedly.
327     # FIXME: it would be better not to insist on getting a
328     # connection here. We should handle this more like the raster
329     # images where the layers etc still are created but are not
330     # drawn in case Thuban can't use the data for various reasons
331     while 1:
332     try:
333     conn = postgisdb.PostGISConnection(**attrs)
334     break
335     except postgisdb.ConnectionError, val:
336     if self.db_connection_callback is not None:
337     attrs = self.db_connection_callback(attrs, str(val))
338     if attrs is None:
339     raise LoadCancelled
340     else:
341     raise
342    
343     self.idmap[ID] = conn
344     self.theSession.AddDBConnection(conn)
345    
346     def start_dbshapesource(self, name, qname, attrs):
347     attrs = self.check_attrs(name, attrs,
348     [AttrDesc("id", True),
349     AttrDesc("dbconn", True,
350     conversion = "idref"),
351     AttrDesc("tablename", True,
352 bh 2104 conversion = "ascii"),
353     # id_column and geometry_column were
354     # newly introduced with thuban-1.1.dtd
355     # where they're required. Since we
356     # support the older formats too we
357     # have them optional here.
358     AttrDesc("id_column", False, "gid",
359     conversion = "ascii"),
360     AttrDesc("geometry_column", False,
361 bh 1646 conversion = "ascii")])
362 bh 2104 # The default value of geometry_column to use when instantiating
363     # the db shapestore is None which we currently can't easily use
364     # in check_attrs
365     geometry_column = attrs["geometry_column"]
366     if not geometry_column:
367     geometry_column = None
368     dbopen = self.theSession.OpenDBShapeStore
369     self.idmap[attrs["id"]] = dbopen(attrs["dbconn"], attrs["tablename"],
370     id_column = attrs["id_column"],
371     geometry_column=geometry_column)
372 bh 1646
373 bh 1268 def start_fileshapesource(self, name, qname, attrs):
374     attrs = self.check_attrs(name, attrs,
375     [AttrDesc("id", True),
376     AttrDesc("filename", True,
377     conversion = "filename"),
378     AttrDesc("filetype", True)])
379     ID = attrs["id"]
380     filename = attrs["filename"]
381     filetype = attrs["filetype"]
382     if filetype != "shapefile":
383     raise LoadError("shapesource filetype %r not supported" % filetype)
384 frank 2446 self.idmap[ID] = self.open_shapefile(filename)
385 bh 1268
386     def start_derivedshapesource(self, name, qname, attrs):
387     attrs = self.check_attrs(name, attrs,
388     [AttrDesc("id", True),
389     AttrDesc("shapesource", True,
390     conversion = "shapesource"),
391     AttrDesc("table", True, conversion="table")])
392 bh 1282 store = DerivedShapeStore(attrs["shapesource"], attrs["table"])
393     self.theSession.AddShapeStore(store)
394     self.idmap[attrs["id"]] = store
395 bh 1268
396     def start_filetable(self, name, qname, attrs):
397     attrs = self.check_attrs(name, attrs,
398     [AttrDesc("id", True),
399     AttrDesc("title", True),
400     AttrDesc("filename", True,
401     conversion = "filename"),
402     AttrDesc("filetype")])
403     filetype = attrs["filetype"]
404     if filetype != "DBF":
405     raise LoadError("shapesource filetype %r not supported" % filetype)
406     table = DBFTable(attrs["filename"])
407     table.SetTitle(attrs["title"])
408     self.idmap[attrs["id"]] = self.theSession.AddTable(table)
409    
410     def start_jointable(self, name, qname, attrs):
411     attrs = self.check_attrs(name, attrs,
412     [AttrDesc("id", True),
413     AttrDesc("title", True),
414     AttrDesc("left", True, conversion="table"),
415     AttrDesc("leftcolumn", True),
416     AttrDesc("right", True, conversion="table"),
417 bh 1375 AttrDesc("rightcolumn", True),
418    
419     # jointype is required for file
420     # version 0.9 but this attribute
421     # wasn't in the 0.8 version because of
422     # an oversight so we assume it's
423     # optional since we want to handle
424     # both file format versions here.
425     AttrDesc("jointype", False,
426     default="INNER")])
427    
428     jointype = attrs["jointype"]
429     if jointype == "LEFT OUTER":
430     outer_join = True
431     elif jointype == "INNER":
432     outer_join = False
433     else:
434     raise LoadError("jointype %r not supported" % jointype )
435 bh 1268 table = TransientJoinedTable(self.theSession.TransientDB(),
436     attrs["left"], attrs["leftcolumn"],
437 bh 1375 attrs["right"], attrs["rightcolumn"],
438     outer_join = outer_join)
439 bh 1268 table.SetTitle(attrs["title"])
440     self.idmap[attrs["id"]] = self.theSession.AddTable(table)
441    
442 bh 267 def start_map(self, name, qname, attrs):
443     """Start a map."""
444 frank 1408 self.aMap = Map(self.encode(attrs.get((None, 'title'), None)))
445 bh 267
446     def end_map(self, name, qname):
447     self.theSession.AddMap(self.aMap)
448 jonathan 874 self.aMap = None
449 bh 267
450     def start_projection(self, name, qname, attrs):
451 bh 1844 attrs = self.check_attrs(name, attrs,
452     [AttrDesc("name", conversion=self.encode),
453     AttrDesc("epsg", default=None,
454     conversion=self.encode)])
455     self.projection_name = attrs["name"]
456     self.projection_epsg = attrs["epsg"]
457     self.projection_params = [ ]
458 bh 267
459     def end_projection(self, name, qname):
460 jonathan 874 if self.aLayer is not None:
461     obj = self.aLayer
462     elif self.aMap is not None:
463     obj = self.aMap
464     else:
465     assert False, "projection tag out of context"
466     pass
467    
468 bh 1844 obj.SetProjection(Projection(self.projection_params,
469     self.projection_name,
470     epsg = self.projection_epsg))
471 bh 267
472     def start_parameter(self, name, qname, attrs):
473     s = attrs.get((None, 'value'))
474     s = str(s) # we can't handle unicode in proj
475 bh 1844 self.projection_params.append(s)
476 bh 267
477     def start_layer(self, name, qname, attrs, layer_class = Layer):
478     """Start a layer
479    
480     Instantiate a layer of class layer_class from the attributes in
481     attrs which may be a dictionary as well as the normal SAX attrs
482     object and bind it to self.aLayer.
483     """
484 jonathan 874 title = self.encode(attrs.get((None, 'title'), ""))
485 bh 267 filename = attrs.get((None, 'filename'), "")
486 jonathan 694 filename = os.path.join(self.GetDirectory(), filename)
487 jonathan 930 filename = self.encode(filename)
488     visible = self.encode(attrs.get((None, 'visible'), "true")) != "false"
489 bh 267 fill = parse_color(attrs.get((None, 'fill'), "None"))
490     stroke = parse_color(attrs.get((None, 'stroke'), "#000000"))
491     stroke_width = int(attrs.get((None, 'stroke_width'), "1"))
492 bh 1268 if attrs.has_key((None, "shapestore")):
493     store = self.idmap[attrs[(None, "shapestore")]]
494     else:
495 frank 2446 store = self.open_shapefile(filename)
496    
497 bh 1268 self.aLayer = layer_class(title, store,
498 bh 723 fill = fill, stroke = stroke,
499 jonathan 772 lineWidth = stroke_width,
500 jonathan 930 visible = visible)
501 bh 267
502     def end_layer(self, name, qname):
503     self.aMap.AddLayer(self.aLayer)
504 jonathan 874 self.aLayer = None
505 bh 267
506 jonathan 930 def start_rasterlayer(self, name, qname, attrs, layer_class = RasterLayer):
507     title = self.encode(attrs.get((None, 'title'), ""))
508     filename = attrs.get((None, 'filename'), "")
509     filename = os.path.join(self.GetDirectory(), filename)
510     filename = self.encode(filename)
511     visible = self.encode(attrs.get((None, 'visible'), "true")) != "false"
512    
513     self.aLayer = layer_class(title, filename, visible = visible)
514    
515     def end_rasterlayer(self, name, qname):
516     self.aMap.AddLayer(self.aLayer)
517     self.aLayer = None
518    
519 jonathan 365 def start_classification(self, name, qname, attrs):
520 bh 1970 attrs = self.check_attrs(name, attrs,
521     [AttrDesc("field", True),
522     AttrDesc("field_type", True)])
523     field = attrs["field"]
524     fieldType = attrs["field_type"]
525 jonathan 465
526     dbFieldType = self.aLayer.GetFieldType(field)
527    
528     if fieldType != dbFieldType:
529     raise ValueError(_("xml field type differs from database!"))
530    
531     # setup conversion routines depending on the kind of data
532     # we will be seeing later on
533     if fieldType == FIELDTYPE_STRING:
534     self.conv = str
535     elif fieldType == FIELDTYPE_INT:
536     self.conv = lambda p: int(float(p))
537     elif fieldType == FIELDTYPE_DOUBLE:
538     self.conv = float
539    
540 bh 1452 self.aLayer.SetClassificationColumn(field)
541 jonathan 465
542 jonathan 365 def end_classification(self, name, qname):
543     pass
544    
545     def start_clnull(self, name, qname, attrs):
546 jonathan 439 self.cl_group = ClassGroupDefault()
547 jonathan 874 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
548 jonathan 439 self.cl_prop = ClassGroupProperties()
549 jonathan 365
550     def end_clnull(self, name, qname):
551 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
552     self.aLayer.GetClassification().SetDefaultGroup(self.cl_group)
553     del self.cl_group, self.cl_prop
554 jonathan 365
555     def start_clpoint(self, name, qname, attrs):
556     attrib_value = attrs.get((None, 'value'), "0")
557    
558 bh 1452 field = self.aLayer.GetClassificationColumn()
559 jonathan 1428 if self.aLayer.GetFieldType(field) == FIELDTYPE_STRING:
560 bh 1417 value = self.encode(attrib_value)
561     else:
562     value = self.conv(attrib_value)
563 jonathan 439 self.cl_group = ClassGroupSingleton(value)
564 jonathan 874 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
565 jonathan 439 self.cl_prop = ClassGroupProperties()
566 jonathan 413
567 jonathan 365
568     def end_clpoint(self, name, qname):
569 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
570 jonathan 614 self.aLayer.GetClassification().AppendGroup(self.cl_group)
571 jonathan 439 del self.cl_group, self.cl_prop
572 jonathan 365
573     def start_clrange(self, name, qname, attrs):
574    
575 jonathan 874 range = attrs.get((None, 'range'), None)
576     # for backward compatibility (min/max are not saved)
577     min = attrs.get((None, 'min'), None)
578     max = attrs.get((None, 'max'), None)
579    
580 jonathan 365 try:
581 jonathan 874 if range is not None:
582     self.cl_group = ClassGroupRange(Range(range))
583     elif min is not None and max is not None:
584 jonathan 1354 self.cl_group = ClassGroupRange((self.conv(min),
585     self.conv(max)))
586 jonathan 874 else:
587     self.cl_group = ClassGroupRange(Range(None))
588    
589 jonathan 365 except ValueError:
590 jan 374 raise ValueError(_("Classification range is not a number!"))
591 jonathan 365
592 jonathan 439 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
593     self.cl_prop = ClassGroupProperties()
594 jonathan 413
595 jonathan 365
596     def end_clrange(self, name, qname):
597 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
598 jonathan 614 self.aLayer.GetClassification().AppendGroup(self.cl_group)
599 jonathan 439 del self.cl_group, self.cl_prop
600 jonathan 365
601     def start_cldata(self, name, qname, attrs):
602 jonathan 465 self.cl_prop.SetLineColor(
603     parse_color(attrs.get((None, 'stroke'), "None")))
604     self.cl_prop.SetLineWidth(
605 jonathan 390 int(attrs.get((None, 'stroke_width'), "0")))
606 jan 2375 self.cl_prop.SetSize(int(attrs.get((None, 'size'), "5")))
607 jonathan 439 self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
608 jonathan 365
609     def end_cldata(self, name, qname):
610     pass
611    
612 bh 267 def start_labellayer(self, name, qname, attrs):
613     self.aLayer = self.aMap.LabelLayer()
614    
615     def start_label(self, name, qname, attrs):
616 bh 2034 attrs = self.check_attrs(name, attrs,
617     [AttrDesc("x", True, conversion = float),
618     AttrDesc("y", True, conversion = float),
619     AttrDesc("text", True),
620     AttrDesc("halign", True,
621     conversion = "ascii"),
622     AttrDesc("valign", True,
623     conversion = "ascii")])
624     x = attrs['x']
625     y = attrs['y']
626     text = attrs['text']
627     halign = attrs['halign']
628     valign = attrs['valign']
629     if halign not in ("left", "center", "right"):
630     raise LoadError("Unsupported halign value %r" % halign)
631     if valign not in ("top", "center", "bottom"):
632     raise LoadError("Unsupported valign value %r" % valign)
633 bh 267 self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)
634    
635     def characters(self, chars):
636     pass
637    
638    
639 frank 2446 def load_session(filename, db_connection_callback = None,
640     shapefile_callback = None):
641 bh 1646 """Load a Thuban session from the file object file
642 jonathan 694
643 bh 1646 The db_connection_callback, if given should be a callable object
644     that can be called like this:
645     db_connection_callback(params, message)
646    
647     where params is a dictionary containing the known connection
648     parameters and message is a string with a message why the connection
649     failed. db_connection_callback should return a new dictionary with
650     corrected and perhaps additional parameters like a password or None
651     to indicate that the user cancelled.
652     """
653 frank 2446 handler = SessionLoader(db_connection_callback, shapefile_callback)
654 jonathan 706 handler.read(filename)
655 jonathan 694
656 bh 6 session = handler.theSession
657     # Newly loaded session aren't modified
658     session.UnsetModified()
659    
660 bh 1930 handler.Destroy()
661    
662 bh 6 return session
663    

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26