/[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 2658 - (hide annotations)
Wed Jul 27 21:50:30 2005 UTC (19 years, 7 months ago) by jan
Original Path: trunk/thuban/Thuban/Model/load.py
File MIME type: text/x-python
File size: 28221 byte(s)
(SessionLoader.start_classification): Change attribute 'field' and
'field_type' from obligatory to optional to allow empty classes
(ie.  only with a default=clnull).

1 bh 2642 # Copyright (C) 2001, 2002, 2003, 2004, 2005 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 bh 2642 self.encode(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     else:
305     raise
306     return store
307    
308 bh 1646 def start_dbconnection(self, name, qname, attrs):
309     attrs = self.check_attrs(name, attrs,
310     [AttrDesc("id", True),
311     AttrDesc("dbtype", True),
312     AttrDesc("host", False, ""),
313     AttrDesc("port", False, ""),
314     AttrDesc("user", False, ""),
315     AttrDesc("dbname", True)])
316     ID = attrs["id"]
317     dbtype = attrs["dbtype"]
318     if dbtype != "postgis":
319     raise LoadError("dbtype %r not supported" % filetype)
320    
321     del attrs["id"]
322     del attrs["dbtype"]
323    
324     # Try to open the connection and if it fails ask the user for
325     # the correct parameters repeatedly.
326     # FIXME: it would be better not to insist on getting a
327     # connection here. We should handle this more like the raster
328     # images where the layers etc still are created but are not
329     # drawn in case Thuban can't use the data for various reasons
330     while 1:
331     try:
332     conn = postgisdb.PostGISConnection(**attrs)
333     break
334     except postgisdb.ConnectionError, val:
335     if self.db_connection_callback is not None:
336     attrs = self.db_connection_callback(attrs, str(val))
337     if attrs is None:
338     raise LoadCancelled
339     else:
340     raise
341    
342     self.idmap[ID] = conn
343     self.theSession.AddDBConnection(conn)
344    
345     def start_dbshapesource(self, name, qname, attrs):
346     attrs = self.check_attrs(name, attrs,
347     [AttrDesc("id", True),
348     AttrDesc("dbconn", True,
349     conversion = "idref"),
350     AttrDesc("tablename", True,
351 bh 2104 conversion = "ascii"),
352     # id_column and geometry_column were
353     # newly introduced with thuban-1.1.dtd
354     # where they're required. Since we
355     # support the older formats too we
356     # have them optional here.
357     AttrDesc("id_column", False, "gid",
358     conversion = "ascii"),
359     AttrDesc("geometry_column", False,
360 bh 1646 conversion = "ascii")])
361 bh 2104 # The default value of geometry_column to use when instantiating
362     # the db shapestore is None which we currently can't easily use
363     # in check_attrs
364     geometry_column = attrs["geometry_column"]
365     if not geometry_column:
366     geometry_column = None
367     dbopen = self.theSession.OpenDBShapeStore
368     self.idmap[attrs["id"]] = dbopen(attrs["dbconn"], attrs["tablename"],
369     id_column = attrs["id_column"],
370     geometry_column=geometry_column)
371 bh 1646
372 bh 1268 def start_fileshapesource(self, name, qname, attrs):
373     attrs = self.check_attrs(name, attrs,
374     [AttrDesc("id", True),
375     AttrDesc("filename", True,
376     conversion = "filename"),
377     AttrDesc("filetype", True)])
378     ID = attrs["id"]
379     filename = attrs["filename"]
380     filetype = attrs["filetype"]
381     if filetype != "shapefile":
382     raise LoadError("shapesource filetype %r not supported" % filetype)
383 frank 2446 self.idmap[ID] = self.open_shapefile(filename)
384 bh 1268
385     def start_derivedshapesource(self, name, qname, attrs):
386     attrs = self.check_attrs(name, attrs,
387     [AttrDesc("id", True),
388     AttrDesc("shapesource", True,
389     conversion = "shapesource"),
390     AttrDesc("table", True, conversion="table")])
391 bh 1282 store = DerivedShapeStore(attrs["shapesource"], attrs["table"])
392     self.theSession.AddShapeStore(store)
393     self.idmap[attrs["id"]] = store
394 bh 1268
395     def start_filetable(self, name, qname, attrs):
396     attrs = self.check_attrs(name, attrs,
397     [AttrDesc("id", True),
398     AttrDesc("title", True),
399     AttrDesc("filename", True,
400     conversion = "filename"),
401     AttrDesc("filetype")])
402     filetype = attrs["filetype"]
403     if filetype != "DBF":
404     raise LoadError("shapesource filetype %r not supported" % filetype)
405     table = DBFTable(attrs["filename"])
406     table.SetTitle(attrs["title"])
407     self.idmap[attrs["id"]] = self.theSession.AddTable(table)
408    
409     def start_jointable(self, name, qname, attrs):
410     attrs = self.check_attrs(name, attrs,
411     [AttrDesc("id", True),
412     AttrDesc("title", True),
413     AttrDesc("left", True, conversion="table"),
414     AttrDesc("leftcolumn", True),
415     AttrDesc("right", True, conversion="table"),
416 bh 1375 AttrDesc("rightcolumn", True),
417    
418     # jointype is required for file
419     # version 0.9 but this attribute
420     # wasn't in the 0.8 version because of
421     # an oversight so we assume it's
422     # optional since we want to handle
423     # both file format versions here.
424     AttrDesc("jointype", False,
425     default="INNER")])
426    
427     jointype = attrs["jointype"]
428     if jointype == "LEFT OUTER":
429     outer_join = True
430     elif jointype == "INNER":
431     outer_join = False
432     else:
433     raise LoadError("jointype %r not supported" % jointype )
434 bh 1268 table = TransientJoinedTable(self.theSession.TransientDB(),
435     attrs["left"], attrs["leftcolumn"],
436 bh 1375 attrs["right"], attrs["rightcolumn"],
437     outer_join = outer_join)
438 bh 1268 table.SetTitle(attrs["title"])
439     self.idmap[attrs["id"]] = self.theSession.AddTable(table)
440    
441 bh 267 def start_map(self, name, qname, attrs):
442     """Start a map."""
443 frank 1408 self.aMap = Map(self.encode(attrs.get((None, 'title'), None)))
444 bh 267
445     def end_map(self, name, qname):
446     self.theSession.AddMap(self.aMap)
447 jonathan 874 self.aMap = None
448 bh 267
449     def start_projection(self, name, qname, attrs):
450 bh 1844 attrs = self.check_attrs(name, attrs,
451     [AttrDesc("name", conversion=self.encode),
452     AttrDesc("epsg", default=None,
453     conversion=self.encode)])
454     self.projection_name = attrs["name"]
455     self.projection_epsg = attrs["epsg"]
456     self.projection_params = [ ]
457 bh 267
458     def end_projection(self, name, qname):
459 jonathan 874 if self.aLayer is not None:
460     obj = self.aLayer
461     elif self.aMap is not None:
462     obj = self.aMap
463     else:
464     assert False, "projection tag out of context"
465     pass
466    
467 bh 1844 obj.SetProjection(Projection(self.projection_params,
468     self.projection_name,
469     epsg = self.projection_epsg))
470 bh 267
471     def start_parameter(self, name, qname, attrs):
472     s = attrs.get((None, 'value'))
473     s = str(s) # we can't handle unicode in proj
474 bh 1844 self.projection_params.append(s)
475 bh 267
476     def start_layer(self, name, qname, attrs, layer_class = Layer):
477     """Start a layer
478    
479     Instantiate a layer of class layer_class from the attributes in
480     attrs which may be a dictionary as well as the normal SAX attrs
481     object and bind it to self.aLayer.
482     """
483 jonathan 874 title = self.encode(attrs.get((None, 'title'), ""))
484 bh 267 filename = attrs.get((None, 'filename'), "")
485 jonathan 694 filename = os.path.join(self.GetDirectory(), filename)
486 jonathan 930 filename = self.encode(filename)
487     visible = self.encode(attrs.get((None, 'visible'), "true")) != "false"
488 bh 267 fill = parse_color(attrs.get((None, 'fill'), "None"))
489     stroke = parse_color(attrs.get((None, 'stroke'), "#000000"))
490     stroke_width = int(attrs.get((None, 'stroke_width'), "1"))
491 bh 1268 if attrs.has_key((None, "shapestore")):
492     store = self.idmap[attrs[(None, "shapestore")]]
493     else:
494 frank 2446 store = self.open_shapefile(filename)
495    
496 bh 1268 self.aLayer = layer_class(title, store,
497 bh 723 fill = fill, stroke = stroke,
498 jonathan 772 lineWidth = stroke_width,
499 jonathan 930 visible = visible)
500 bh 267
501     def end_layer(self, name, qname):
502     self.aMap.AddLayer(self.aLayer)
503 jonathan 874 self.aLayer = None
504 bh 267
505 jonathan 930 def start_rasterlayer(self, name, qname, attrs, layer_class = RasterLayer):
506     title = self.encode(attrs.get((None, 'title'), ""))
507     filename = attrs.get((None, 'filename'), "")
508     filename = os.path.join(self.GetDirectory(), filename)
509     filename = self.encode(filename)
510     visible = self.encode(attrs.get((None, 'visible'), "true")) != "false"
511 jonathan 2615 opacity = float(attrs.get((None, 'opacity'), "1"))
512     masktype = str(attrs.get((None, 'masktype'), "bit"))
513 jonathan 930
514 jonathan 2615 masktypes = {"none": layer_class.MASK_NONE,
515     "bit": layer_class.MASK_BIT,
516     "alpha": layer_class.MASK_ALPHA}
517 jonathan 930
518 jonathan 2615 self.aLayer = layer_class(title, filename,
519     visible = visible,
520     opacity = opacity,
521     masktype = masktypes[masktype])
522    
523 jonathan 930 def end_rasterlayer(self, name, qname):
524     self.aMap.AddLayer(self.aLayer)
525     self.aLayer = None
526    
527 jonathan 365 def start_classification(self, name, qname, attrs):
528 jan 2658 # field and field_type are optional because the classification
529     # can also be empty, ie. have only a default.
530 bh 1970 attrs = self.check_attrs(name, attrs,
531 jan 2658 [AttrDesc("field", False),
532     AttrDesc("field_type", False)])
533    
534 bh 1970 field = attrs["field"]
535     fieldType = attrs["field_type"]
536 jonathan 465
537 jan 2658 if field == "": return # no need to set classification column.
538    
539 jonathan 465 dbFieldType = self.aLayer.GetFieldType(field)
540    
541     if fieldType != dbFieldType:
542     raise ValueError(_("xml field type differs from database!"))
543    
544     # setup conversion routines depending on the kind of data
545     # we will be seeing later on
546     if fieldType == FIELDTYPE_STRING:
547     self.conv = str
548     elif fieldType == FIELDTYPE_INT:
549     self.conv = lambda p: int(float(p))
550     elif fieldType == FIELDTYPE_DOUBLE:
551     self.conv = float
552    
553 bh 1452 self.aLayer.SetClassificationColumn(field)
554 jonathan 465
555 jonathan 365 def end_classification(self, name, qname):
556     pass
557    
558     def start_clnull(self, name, qname, attrs):
559 jonathan 439 self.cl_group = ClassGroupDefault()
560 jonathan 874 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
561 jonathan 439 self.cl_prop = ClassGroupProperties()
562 jonathan 365
563     def end_clnull(self, name, qname):
564 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
565     self.aLayer.GetClassification().SetDefaultGroup(self.cl_group)
566     del self.cl_group, self.cl_prop
567 jonathan 365
568     def start_clpoint(self, name, qname, attrs):
569     attrib_value = attrs.get((None, 'value'), "0")
570    
571 bh 1452 field = self.aLayer.GetClassificationColumn()
572 jonathan 1428 if self.aLayer.GetFieldType(field) == FIELDTYPE_STRING:
573 bh 1417 value = self.encode(attrib_value)
574     else:
575     value = self.conv(attrib_value)
576 jonathan 439 self.cl_group = ClassGroupSingleton(value)
577 jonathan 874 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
578 jonathan 439 self.cl_prop = ClassGroupProperties()
579 jonathan 413
580 jonathan 365
581     def end_clpoint(self, name, qname):
582 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
583 jonathan 614 self.aLayer.GetClassification().AppendGroup(self.cl_group)
584 jonathan 439 del self.cl_group, self.cl_prop
585 jonathan 365
586     def start_clrange(self, name, qname, attrs):
587 bh 2642 attrs = self.check_attrs(name, attrs,
588     [AttrDesc("range", False, None),
589     AttrDesc("min", False, None),
590     AttrDesc("max", False, None)])
591 jonathan 365
592 bh 2642 range = attrs['range']
593 jonathan 874 # for backward compatibility (min/max are not saved)
594 bh 2642 min = attrs['min']
595     max = attrs['max']
596 jonathan 874
597 jonathan 365 try:
598 jonathan 874 if range is not None:
599     self.cl_group = ClassGroupRange(Range(range))
600     elif min is not None and max is not None:
601 jonathan 1354 self.cl_group = ClassGroupRange((self.conv(min),
602     self.conv(max)))
603 jonathan 874 else:
604     self.cl_group = ClassGroupRange(Range(None))
605    
606 jonathan 365 except ValueError:
607 jan 374 raise ValueError(_("Classification range is not a number!"))
608 jonathan 365
609 jonathan 439 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
610     self.cl_prop = ClassGroupProperties()
611 jonathan 413
612 jonathan 365
613     def end_clrange(self, name, qname):
614 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
615 jonathan 614 self.aLayer.GetClassification().AppendGroup(self.cl_group)
616 jonathan 439 del self.cl_group, self.cl_prop
617 jonathan 365
618     def start_cldata(self, name, qname, attrs):
619 jonathan 465 self.cl_prop.SetLineColor(
620     parse_color(attrs.get((None, 'stroke'), "None")))
621     self.cl_prop.SetLineWidth(
622 jonathan 390 int(attrs.get((None, 'stroke_width'), "0")))
623 jan 2375 self.cl_prop.SetSize(int(attrs.get((None, 'size'), "5")))
624 jonathan 439 self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
625 jonathan 365
626     def end_cldata(self, name, qname):
627     pass
628    
629 bh 267 def start_labellayer(self, name, qname, attrs):
630     self.aLayer = self.aMap.LabelLayer()
631    
632     def start_label(self, name, qname, attrs):
633 bh 2034 attrs = self.check_attrs(name, attrs,
634     [AttrDesc("x", True, conversion = float),
635     AttrDesc("y", True, conversion = float),
636     AttrDesc("text", True),
637     AttrDesc("halign", True,
638     conversion = "ascii"),
639     AttrDesc("valign", True,
640     conversion = "ascii")])
641     x = attrs['x']
642     y = attrs['y']
643     text = attrs['text']
644     halign = attrs['halign']
645     valign = attrs['valign']
646     if halign not in ("left", "center", "right"):
647     raise LoadError("Unsupported halign value %r" % halign)
648     if valign not in ("top", "center", "bottom"):
649     raise LoadError("Unsupported valign value %r" % valign)
650 bh 267 self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)
651    
652     def characters(self, chars):
653     pass
654    
655    
656 frank 2446 def load_session(filename, db_connection_callback = None,
657     shapefile_callback = None):
658 bh 1646 """Load a Thuban session from the file object file
659 jonathan 694
660 bh 1646 The db_connection_callback, if given should be a callable object
661     that can be called like this:
662     db_connection_callback(params, message)
663    
664     where params is a dictionary containing the known connection
665     parameters and message is a string with a message why the connection
666     failed. db_connection_callback should return a new dictionary with
667     corrected and perhaps additional parameters like a password or None
668     to indicate that the user cancelled.
669     """
670 frank 2446 handler = SessionLoader(db_connection_callback, shapefile_callback)
671 jonathan 706 handler.read(filename)
672 jonathan 694
673 bh 6 session = handler.theSession
674     # Newly loaded session aren't modified
675     session.UnsetModified()
676    
677 bh 1930 handler.Destroy()
678    
679 bh 6 return session
680    

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26