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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26