/[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 1354 - (hide annotations)
Wed Jul 2 09:37:07 2003 UTC (21 years, 8 months ago) by jonathan
Original Path: trunk/thuban/Thuban/Model/load.py
File MIME type: text/x-python
File size: 17102 byte(s)
(SessionLoader.start_clrange): Fix
        call to ClassGroupRange constructor to use a tuple.

1 bh 723 # Copyright (C) 2001, 2002, 2003 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 bh 6 #
7     # This program is free software under the GPL (>=v2)
8     # Read the file COPYING coming with GRASS for details.
9    
10     """
11     Parser for thuban session files.
12     """
13    
14     __version__ = "$Revision$"
15    
16 bh 723 import string, os
17 bh 267
18     import xml.sax
19     import xml.sax.handler
20 jonathan 526 from xml.sax import make_parser, ErrorHandler, SAXNotRecognizedException
21 bh 267
22 jan 374 from Thuban import _
23 jonathan 413
24 jonathan 473 from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
25     FIELDTYPE_STRING
26    
27 jonathan 1339 from Thuban.Model.color import Color, Transparent
28    
29 bh 6 from Thuban.Model.session import Session
30     from Thuban.Model.map import Map
31 jonathan 930 from Thuban.Model.layer import Layer, RasterLayer
32 bh 6 from Thuban.Model.proj import Projection
33 jonathan 874 from Thuban.Model.range import Range
34 jonathan 413 from Thuban.Model.classification import Classification, \
35 jonathan 439 ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
36     ClassGroupProperties
37 bh 1268 from Thuban.Model.data import DerivedShapeStore, ShapefileStore
38     from Thuban.Model.table import DBFTable
39     from Thuban.Model.transientdb import TransientJoinedTable
40 bh 6
41 bh 1268 class LoadError(Exception):
42     pass
43    
44 jonathan 1159 from Thuban.Model.xmlreader import XMLReader
45     import resource
46 bh 6
47 bh 267 def parse_color(color):
48     """Return the color object for the string color.
49 bh 6
50 bh 267 Color may be either 'None' or of the form '#RRGGBB' in the usual
51     HTML color notation
52 bh 6 """
53     color = string.strip(color)
54     if color == "None":
55 jonathan 1339 result = Transparent
56 bh 6 elif color[0] == '#':
57     if len(color) == 7:
58     r = string.atoi(color[1:3], 16) / 255.0
59     g = string.atoi(color[3:5], 16) / 255.0
60     b = string.atoi(color[5:7], 16) / 255.0
61     result = Color(r, g, b)
62     else:
63 jan 374 raise ValueError(_("Invalid hexadecimal color specification %s")
64 bh 6 % color)
65     else:
66 jan 374 raise ValueError(_("Invalid color specification %s") % color)
67 bh 6 return result
68    
69 bh 1268 class AttrDesc:
70    
71     def __init__(self, name, required = False, default = "",
72     conversion = None):
73     if not isinstance(name, tuple):
74     fullname = (None, name)
75     else:
76     fullname = name
77     name = name[1]
78     self.name = name
79     self.fullname = fullname
80     self.required = required
81     self.default = default
82     self.conversion = conversion
83    
84     # set by the SessionLoader's check_attrs method
85     self.value = None
86    
87    
88 jonathan 706 class SessionLoader(XMLReader):
89 jonathan 694
90 jonathan 706 def __init__(self):
91 jonathan 694 """Inititialize the Sax handler."""
92 jonathan 706 XMLReader.__init__(self)
93 jonathan 694
94     self.theSession = None
95     self.aMap = None
96     self.aLayer = None
97    
98 bh 1268 # Map ids used in the thuban file to the corresponding objects
99     # in the session
100     self.idmap = {}
101 jonathan 706
102 bh 1268 dispatchers = {
103     'session' : ("start_session", "end_session"),
104     'fileshapesource': ("start_fileshapesource", None),
105     'derivedshapesource': ("start_derivedshapesource", None),
106     'filetable': ("start_filetable", None),
107     'jointable': ("start_jointable", None),
108    
109     'map' : ("start_map", "end_map"),
110     'projection' : ("start_projection", "end_projection"),
111     'parameter' : ("start_parameter", None),
112     'layer' : ("start_layer", "end_layer"),
113     'rasterlayer' : ("start_rasterlayer", "end_rasterlayer"),
114     'classification': ("start_classification", "end_classification"),
115     'clnull' : ("start_clnull", "end_clnull"),
116     'clpoint' : ("start_clpoint", "end_clpoint"),
117     'clrange' : ("start_clrange", "end_clrange"),
118     'cldata' : ("start_cldata", "end_cldata"),
119     'table' : ("start_table", "end_table"),
120     'labellayer' : ("start_labellayer", None),
121     'label' : ("start_label", None)}
122    
123     # all dispatchers should be used for the 0.8 namespace
124     xmlns = "http://thuban.intevation.org/dtds/thuban-0.8.dtd"
125     for key, value in dispatchers.items():
126     dispatchers[(xmlns, key)] = value
127    
128     XMLReader.AddDispatchers(self, dispatchers)
129    
130 bh 267 def start_session(self, name, qname, attrs):
131 bh 1268 self.theSession = Session(self.encode(attrs.get((None, 'title'),
132     None)))
133 bh 6
134 bh 267 def end_session(self, name, qname):
135     pass
136 bh 6
137 bh 1268 def check_attrs(self, element, attrs, descr):
138     """Check and convert some of the attributes of an element
139    
140     Parameters:
141     element -- The element name
142     attrs -- The attrs mapping as passed to the start_* methods
143     descr -- Sequence of attribute descriptions (AttrDesc instances)
144    
145     Return a dictionary containig normalized versions of the
146     attributes described in descr. The keys of that dictionary are
147     the name attributes of the attribute descriptions. The attrs
148     dictionary will not be modified.
149    
150     If the attribute is required, i.e. the 'required' attribute of
151     the descrtiption is true, but it is not in attrs, raise a
152     LoadError.
153    
154     If the attribute has a default value and it is not present in
155     attrs, use that default value as the value in the returned dict.
156    
157     If a conversion is specified, convert the value before putting
158     it into the returned dict. The following conversions are
159     available:
160    
161     'filename' -- The attribute is a filename.
162    
163     If the filename is a relative name, interpret
164     it relative to the directory containing the
165     .thuban file and make it an absolute name
166    
167     'shapestore' -- The attribute is the ID of a shapestore
168     defined earlier in the .thuban file. Look it
169     up self.idmap
170    
171     'table' -- The attribute is the ID of a table or shapestore
172     defined earlier in the .thuban file. Look it up
173     self.idmap. If it's the ID of a shapestore the
174     value will be the table of the shapestore.
175     """
176     normalized = {}
177    
178     for d in descr:
179     if d.required and not attrs.has_key(d.fullname):
180     pass
181     #raise LoadError("Element %s requires an attribute %r"
182     # % (element, d.name))
183     value = attrs.get(d.fullname, d.default)
184    
185     if d.conversion == "shapesource":
186     if value in self.idmap:
187     value = self.idmap[value]
188     else:
189     raise LoadError("Element %s requires an already defined ID"
190     " in attribute %r"
191     % (element, d.name))
192     elif d.conversion == "table":
193     if value in self.idmap:
194     value = self.idmap[value]
195     if isinstance(value, ShapefileStore):
196     value = value.Table()
197     else:
198     raise LoadError("Element %s requires an already defined ID"
199     " in attribute %r"
200     % (element, d.name))
201     elif d.conversion == "filename":
202     value = os.path.abspath(os.path.join(self.GetDirectory(),
203     value))
204    
205     normalized[d.name] = value
206     return normalized
207    
208     def start_fileshapesource(self, name, qname, attrs):
209     attrs = self.check_attrs(name, attrs,
210     [AttrDesc("id", True),
211     AttrDesc("filename", True,
212     conversion = "filename"),
213     AttrDesc("filetype", True)])
214     ID = attrs["id"]
215     filename = attrs["filename"]
216     filetype = attrs["filetype"]
217     if filetype != "shapefile":
218     raise LoadError("shapesource filetype %r not supported" % filetype)
219     self.idmap[ID] = self.theSession.OpenShapefile(filename)
220    
221     def start_derivedshapesource(self, name, qname, attrs):
222     attrs = self.check_attrs(name, attrs,
223     [AttrDesc("id", True),
224     AttrDesc("shapesource", True,
225     conversion = "shapesource"),
226     AttrDesc("table", True, conversion="table")])
227 bh 1282 store = DerivedShapeStore(attrs["shapesource"], attrs["table"])
228     self.theSession.AddShapeStore(store)
229     self.idmap[attrs["id"]] = store
230 bh 1268
231     def start_filetable(self, name, qname, attrs):
232     attrs = self.check_attrs(name, attrs,
233     [AttrDesc("id", True),
234     AttrDesc("title", True),
235     AttrDesc("filename", True,
236     conversion = "filename"),
237     AttrDesc("filetype")])
238     filetype = attrs["filetype"]
239     if filetype != "DBF":
240     raise LoadError("shapesource filetype %r not supported" % filetype)
241     table = DBFTable(attrs["filename"])
242     table.SetTitle(attrs["title"])
243     self.idmap[attrs["id"]] = self.theSession.AddTable(table)
244    
245     def start_jointable(self, name, qname, attrs):
246     attrs = self.check_attrs(name, attrs,
247     [AttrDesc("id", True),
248     AttrDesc("title", True),
249     AttrDesc("left", True, conversion="table"),
250     AttrDesc("leftcolumn", True),
251     AttrDesc("right", True, conversion="table"),
252     AttrDesc("rightcolumn")])
253     table = TransientJoinedTable(self.theSession.TransientDB(),
254     attrs["left"], attrs["leftcolumn"],
255     attrs["right"], attrs["rightcolumn"])
256     table.SetTitle(attrs["title"])
257     self.idmap[attrs["id"]] = self.theSession.AddTable(table)
258    
259 bh 267 def start_map(self, name, qname, attrs):
260     """Start a map."""
261     self.aMap = Map(attrs.get((None, 'title'), None))
262    
263     def end_map(self, name, qname):
264     self.theSession.AddMap(self.aMap)
265 jonathan 874 self.aMap = None
266 bh 267
267     def start_projection(self, name, qname, attrs):
268 jonathan 874 self.ProjectionName = self.encode(attrs.get((None, 'name'), None))
269 bh 267 self.ProjectionParams = [ ]
270    
271     def end_projection(self, name, qname):
272 jonathan 874 if self.aLayer is not None:
273     obj = self.aLayer
274     elif self.aMap is not None:
275     obj = self.aMap
276     else:
277     assert False, "projection tag out of context"
278     pass
279    
280     obj.SetProjection(
281 jonathan 744 Projection(self.ProjectionParams, self.ProjectionName))
282 bh 267
283     def start_parameter(self, name, qname, attrs):
284     s = attrs.get((None, 'value'))
285     s = str(s) # we can't handle unicode in proj
286     self.ProjectionParams.append(s)
287    
288     def start_layer(self, name, qname, attrs, layer_class = Layer):
289     """Start a layer
290    
291     Instantiate a layer of class layer_class from the attributes in
292     attrs which may be a dictionary as well as the normal SAX attrs
293     object and bind it to self.aLayer.
294     """
295 jonathan 874 title = self.encode(attrs.get((None, 'title'), ""))
296 bh 267 filename = attrs.get((None, 'filename'), "")
297 jonathan 694 filename = os.path.join(self.GetDirectory(), filename)
298 jonathan 930 filename = self.encode(filename)
299     visible = self.encode(attrs.get((None, 'visible'), "true")) != "false"
300 bh 267 fill = parse_color(attrs.get((None, 'fill'), "None"))
301     stroke = parse_color(attrs.get((None, 'stroke'), "#000000"))
302     stroke_width = int(attrs.get((None, 'stroke_width'), "1"))
303 bh 1268 if attrs.has_key((None, "shapestore")):
304     store = self.idmap[attrs[(None, "shapestore")]]
305     else:
306     store = self.theSession.OpenShapefile(filename)
307     self.aLayer = layer_class(title, store,
308 bh 723 fill = fill, stroke = stroke,
309 jonathan 772 lineWidth = stroke_width,
310 jonathan 930 visible = visible)
311 bh 267
312     def end_layer(self, name, qname):
313     self.aMap.AddLayer(self.aLayer)
314 jonathan 874 self.aLayer = None
315 bh 267
316 jonathan 930 def start_rasterlayer(self, name, qname, attrs, layer_class = RasterLayer):
317     title = self.encode(attrs.get((None, 'title'), ""))
318     filename = attrs.get((None, 'filename'), "")
319     filename = os.path.join(self.GetDirectory(), filename)
320     filename = self.encode(filename)
321     visible = self.encode(attrs.get((None, 'visible'), "true")) != "false"
322    
323     self.aLayer = layer_class(title, filename, visible = visible)
324    
325     def end_rasterlayer(self, name, qname):
326     self.aMap.AddLayer(self.aLayer)
327     self.aLayer = None
328    
329 jonathan 365 def start_classification(self, name, qname, attrs):
330 jonathan 465 field = attrs.get((None, 'field'), None)
331    
332     fieldType = attrs.get((None, 'field_type'), None)
333     dbFieldType = self.aLayer.GetFieldType(field)
334    
335     if fieldType != dbFieldType:
336     raise ValueError(_("xml field type differs from database!"))
337    
338     # setup conversion routines depending on the kind of data
339     # we will be seeing later on
340     if fieldType == FIELDTYPE_STRING:
341     self.conv = str
342     elif fieldType == FIELDTYPE_INT:
343     self.conv = lambda p: int(float(p))
344     elif fieldType == FIELDTYPE_DOUBLE:
345     self.conv = float
346    
347 jonathan 1339 self.aLayer.GetClassification().SetFieldInfo(field, fieldType)
348 jonathan 465
349 jonathan 365 def end_classification(self, name, qname):
350     pass
351    
352     def start_clnull(self, name, qname, attrs):
353 jonathan 439 self.cl_group = ClassGroupDefault()
354 jonathan 874 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
355 jonathan 439 self.cl_prop = ClassGroupProperties()
356 jonathan 365
357     def end_clnull(self, name, qname):
358 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
359     self.aLayer.GetClassification().SetDefaultGroup(self.cl_group)
360     del self.cl_group, self.cl_prop
361 jonathan 365
362     def start_clpoint(self, name, qname, attrs):
363     attrib_value = attrs.get((None, 'value'), "0")
364    
365 jonathan 465 value = self.conv(attrib_value)
366    
367 jonathan 439 self.cl_group = ClassGroupSingleton(value)
368 jonathan 874 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
369 jonathan 439 self.cl_prop = ClassGroupProperties()
370 jonathan 413
371 jonathan 365
372     def end_clpoint(self, name, qname):
373 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
374 jonathan 614 self.aLayer.GetClassification().AppendGroup(self.cl_group)
375 jonathan 439 del self.cl_group, self.cl_prop
376 jonathan 365
377     def start_clrange(self, name, qname, attrs):
378    
379 jonathan 874 range = attrs.get((None, 'range'), None)
380     # for backward compatibility (min/max are not saved)
381     min = attrs.get((None, 'min'), None)
382     max = attrs.get((None, 'max'), None)
383    
384 jonathan 365 try:
385 jonathan 874 if range is not None:
386     self.cl_group = ClassGroupRange(Range(range))
387     elif min is not None and max is not None:
388 jonathan 1354 self.cl_group = ClassGroupRange((self.conv(min),
389     self.conv(max)))
390 jonathan 874 else:
391     self.cl_group = ClassGroupRange(Range(None))
392    
393 jonathan 365 except ValueError:
394 jan 374 raise ValueError(_("Classification range is not a number!"))
395 jonathan 365
396 jonathan 439 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
397     self.cl_prop = ClassGroupProperties()
398 jonathan 413
399 jonathan 365
400     def end_clrange(self, name, qname):
401 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
402 jonathan 614 self.aLayer.GetClassification().AppendGroup(self.cl_group)
403 jonathan 439 del self.cl_group, self.cl_prop
404 jonathan 365
405     def start_cldata(self, name, qname, attrs):
406 jonathan 465 self.cl_prop.SetLineColor(
407     parse_color(attrs.get((None, 'stroke'), "None")))
408     self.cl_prop.SetLineWidth(
409 jonathan 390 int(attrs.get((None, 'stroke_width'), "0")))
410 jonathan 439 self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
411 jonathan 365
412     def end_cldata(self, name, qname):
413     pass
414    
415 bh 267 def start_labellayer(self, name, qname, attrs):
416     self.aLayer = self.aMap.LabelLayer()
417    
418     def start_label(self, name, qname, attrs):
419     x = float(attrs[(None, 'x')])
420     y = float(attrs[(None, 'y')])
421 jonathan 874 text = self.encode(attrs[(None, 'text')])
422 bh 267 halign = attrs[(None, 'halign')]
423     valign = attrs[(None, 'valign')]
424     self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)
425    
426     def characters(self, chars):
427     pass
428    
429    
430 jonathan 694 def load_session(filename):
431     """Load a Thuban session from the file object file"""
432    
433 jonathan 706 handler = SessionLoader()
434     handler.read(filename)
435 jonathan 694
436 bh 6 session = handler.theSession
437     # Newly loaded session aren't modified
438     session.UnsetModified()
439    
440     return session
441    

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26