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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 722 - (show annotations)
Thu Apr 24 10:12:29 2003 UTC (21 years, 10 months ago) by jonathan
Original Path: trunk/thuban/Thuban/Model/load.py
File MIME type: text/x-python
File size: 12500 byte(s)
(XMLReader.read): Should have been checking if the file_or_filename object
        had the 'read' attribute.

1 # Copyright (C) 2001, 2002 by Intevation GmbH
2 # Authors:
3 # Jan-Oliver Wagner <[email protected]>
4 # Bernhard Herzog <[email protected]>
5 # Jonathan Coles <[email protected]>
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 import sys, string, os
17
18 import xml.sax
19 import xml.sax.handler
20 from xml.sax import make_parser, ErrorHandler, SAXNotRecognizedException
21
22 from Thuban import _
23 from Thuban.common import *
24
25 from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
26 FIELDTYPE_STRING
27
28 from Thuban.Model.session import Session
29 from Thuban.Model.map import Map
30 from Thuban.Model.layer import Layer
31 from Thuban.Model.color import Color
32 from Thuban.Model.proj import Projection
33 from Thuban.Model.classification import Classification, \
34 ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
35 ClassGroupProperties
36
37
38 def parse_color(color):
39 """Return the color object for the string color.
40
41 Color may be either 'None' or of the form '#RRGGBB' in the usual
42 HTML color notation
43 """
44 color = string.strip(color)
45 if color == "None":
46 result = Color.Transparent
47 elif color[0] == '#':
48 if len(color) == 7:
49 r = string.atoi(color[1:3], 16) / 255.0
50 g = string.atoi(color[3:5], 16) / 255.0
51 b = string.atoi(color[5:7], 16) / 255.0
52 result = Color(r, g, b)
53 else:
54 raise ValueError(_("Invalid hexadecimal color specification %s")
55 % color)
56 else:
57 raise ValueError(_("Invalid color specification %s") % color)
58 return result
59
60
61 class XMLReader(xml.sax.handler.ContentHandler):
62
63 # Dictionary mapping element names (or (URI, element name) pairs for
64 # documents using namespaces) to method names. The methods should
65 # accept the same parameters as the startElement (or startElementNS)
66 # methods. The start_dispatcher is used by the default startElement
67 # and startElementNS methods to call a method for the open tag of an
68 # element.
69 start_dispatcher = {}
70
71 # end_dispatcher works just like start_dispatcher but it's used by
72 # endElement and endElementNS. The method whose names it maps to
73 # should accept the same parameters as endElement and endElementNS.
74 end_dispatcher = {}
75
76
77 def __init__(self):
78 self.chars = ''
79 self.__parser = None
80 self.__directory = ""
81 self.__dispatchers = {}
82
83 def read(self, file_or_filename):
84
85 if hasattr(file_or_filename, "read"):
86 # it's a file object
87 self.__directory = ""
88 self.__file = file_or_filename
89 else:
90 filename = file_or_filename
91 self.__directory = os.path.dirname(filename)
92 self.__file = open(filename)
93
94 if self.__parser is None:
95 self.__parser = make_parser()
96 self.__parser.setContentHandler(self)
97 self.__parser.setErrorHandler(ErrorHandler())
98 self.__parser.setFeature(xml.sax.handler.feature_namespaces, 1)
99
100 #
101 # Well, this isn't pretty, but it appears that if you
102 # use Python 2.2 without the site-package _xmlplus then
103 # the following will fail, and without them it will work.
104 # However, if you do have the site-package and you don't
105 # call these functions, the reader raises an exception
106 #
107 # The reason we set these to 0 in the first place is
108 # because there is an unresolved issue with external
109 # entities causing an exception in the reader
110 #
111 try:
112 self.__parser.setFeature(xml.sax.handler.feature_validation,0)
113 self.__parser.setFeature(xml.sax.handler.feature_external_ges,0)
114 self.__parser.setFeature(xml.sax.handler.feature_external_pes,0)
115 except SAXNotRecognizedException:
116 pass
117
118 self.__parser.parse(self.__file)
119
120 self.close()
121
122 def close(self):
123 self.__file.close()
124
125 def GetFileName(self):
126 if hasattr(self.__file, "name"):
127 return self.__file.name
128
129 return ""
130
131 def GetDirectory(self):
132 return self.__directory
133
134
135 def AddDispatchers(self, dict):
136 """Add the function names that should be used to process XML tags.
137
138 dict -- a dictionary whose keys are XML tag strings and whose values
139 are pairs of strings such that the first string is
140 the name of the function that should be called when the
141 XML tag opens and the second string is the name of the
142 function that should be called when the XML tag closes.
143 If a pair element is None, no function is called.
144 """
145
146 self.__dispatchers.update(dict)
147
148 def startElementNS(self, name, qname, attrs):
149 """Call the method given for name in self.start_dispatcher
150 """
151 if name[0] is None:
152 method_name = self.__dispatchers.get(name[1])
153 else:
154 # Dispatch with namespace
155 method_name = self.__dispatchers.get(name)
156 if method_name is not None and method_name[0] is not None:
157 getattr(self, method_name[0])(name, qname, attrs)
158
159 def endElementNS(self, name, qname):
160 """Call the method given for name in self.end_dispatcher
161 """
162 if name[0] is None:
163 method_name = self.__dispatchers.get(name[1])
164 else:
165 # Dispatch with namespace
166 method_name = self.__dispatchers.get(name)
167 if method_name is not None and method_name[1] is not None:
168 getattr(self, method_name[1])(name, qname)
169
170 class SessionLoader(XMLReader):
171
172 def __init__(self):
173 """Inititialize the Sax handler."""
174 XMLReader.__init__(self)
175
176 self.theSession = None
177 self.aMap = None
178 self.aLayer = None
179
180 XMLReader.AddDispatchers(self,
181 {'session' : ("start_session", "end_session"),
182 'map' : ("start_map", "end_map"),
183 'projection' : ("start_projection", "end_projection"),
184 'parameter' : ("start_parameter", None),
185 'layer' : ("start_layer", "end_layer"),
186 'classification': ("start_classification", "end_classification"),
187 'clnull' : ("start_clnull", "end_clnull"),
188 'clpoint' : ("start_clpoint", "end_clpoint"),
189 'clrange' : ("start_clrange", "end_clrange"),
190 'cldata' : ("start_cldata", "end_cldata"),
191 'table' : ("start_table", "end_table"),
192 'labellayer' : ("start_labellayer", None),
193 'label' : ("start_label", None)})
194
195 def start_session(self, name, qname, attrs):
196 self.theSession = Session(attrs.get((None, 'title'), None))
197
198 def end_session(self, name, qname):
199 pass
200
201 def start_map(self, name, qname, attrs):
202 """Start a map."""
203 self.aMap = Map(attrs.get((None, 'title'), None))
204
205 def end_map(self, name, qname):
206 self.theSession.AddMap(self.aMap)
207
208 def start_projection(self, name, qname, attrs):
209 self.ProjectionParams = [ ]
210
211 def end_projection(self, name, qname):
212 self.aMap.SetProjection(Projection(self.ProjectionParams))
213
214 def start_parameter(self, name, qname, attrs):
215 s = attrs.get((None, 'value'))
216 s = str(s) # we can't handle unicode in proj
217 self.ProjectionParams.append(s)
218
219 def start_layer(self, name, qname, attrs, layer_class = Layer):
220 """Start a layer
221
222 Instantiate a layer of class layer_class from the attributes in
223 attrs which may be a dictionary as well as the normal SAX attrs
224 object and bind it to self.aLayer.
225 """
226 title = attrs.get((None, 'title'), "")
227 filename = attrs.get((None, 'filename'), "")
228 filename = os.path.join(self.GetDirectory(), filename)
229 fill = parse_color(attrs.get((None, 'fill'), "None"))
230 stroke = parse_color(attrs.get((None, 'stroke'), "#000000"))
231 stroke_width = int(attrs.get((None, 'stroke_width'), "1"))
232 self.aLayer = layer_class(title, filename, fill = fill,
233 stroke = stroke, lineWidth = stroke_width)
234
235 def end_layer(self, name, qname):
236 self.aMap.AddLayer(self.aLayer)
237
238 def start_classification(self, name, qname, attrs):
239 field = attrs.get((None, 'field'), None)
240
241 fieldType = attrs.get((None, 'field_type'), None)
242 dbFieldType = self.aLayer.GetFieldType(field)
243
244 if fieldType != dbFieldType:
245 raise ValueError(_("xml field type differs from database!"))
246
247 # setup conversion routines depending on the kind of data
248 # we will be seeing later on
249 if fieldType == FIELDTYPE_STRING:
250 self.conv = str
251 elif fieldType == FIELDTYPE_INT:
252 self.conv = lambda p: int(float(p))
253 elif fieldType == FIELDTYPE_DOUBLE:
254 self.conv = float
255
256 self.aLayer.GetClassification().SetField(field)
257
258
259 def end_classification(self, name, qname):
260 pass
261
262 def start_clnull(self, name, qname, attrs):
263 self.cl_group = ClassGroupDefault()
264 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
265 self.cl_prop = ClassGroupProperties()
266
267 def end_clnull(self, name, qname):
268 self.cl_group.SetProperties(self.cl_prop)
269 self.aLayer.GetClassification().SetDefaultGroup(self.cl_group)
270 del self.cl_group, self.cl_prop
271
272 def start_clpoint(self, name, qname, attrs):
273 attrib_value = attrs.get((None, 'value'), "0")
274
275 #try:
276 #value = Str2Num(attrib_value)
277 #except:
278 #value = attrib_value
279
280 value = self.conv(attrib_value)
281
282 self.cl_group = ClassGroupSingleton(value)
283 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
284 self.cl_prop = ClassGroupProperties()
285
286
287 def end_clpoint(self, name, qname):
288 self.cl_group.SetProperties(self.cl_prop)
289 self.aLayer.GetClassification().AppendGroup(self.cl_group)
290 del self.cl_group, self.cl_prop
291
292 def start_clrange(self, name, qname, attrs):
293
294 try:
295 min = self.conv(attrs.get((None, 'min'), "0"))
296 max = self.conv(attrs.get((None, 'max'), "0"))
297 #min = Str2Num(attrs.get((None, 'min'), "0"))
298 #max = Str2Num(attrs.get((None, 'max'), "0"))
299 except ValueError:
300 raise ValueError(_("Classification range is not a number!"))
301
302 self.cl_group = ClassGroupRange(min, max)
303 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
304 self.cl_prop = ClassGroupProperties()
305
306
307 def end_clrange(self, name, qname):
308 self.cl_group.SetProperties(self.cl_prop)
309 self.aLayer.GetClassification().AppendGroup(self.cl_group)
310 del self.cl_group, self.cl_prop
311
312 def start_cldata(self, name, qname, attrs):
313 self.cl_prop.SetLineColor(
314 parse_color(attrs.get((None, 'stroke'), "None")))
315 self.cl_prop.SetLineWidth(
316 int(attrs.get((None, 'stroke_width'), "0")))
317 self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
318
319 def end_cldata(self, name, qname):
320 pass
321
322 def start_table(self, name, qname, attrs):
323 #print "table title: %s" % attrs.get('title', None)
324 pass
325
326 def end_table(self, name, qname):
327 pass
328
329 def start_labellayer(self, name, qname, attrs):
330 self.aLayer = self.aMap.LabelLayer()
331
332 def start_label(self, name, qname, attrs):
333 x = float(attrs[(None, 'x')])
334 y = float(attrs[(None, 'y')])
335 text = attrs[(None, 'text')]
336 halign = attrs[(None, 'halign')]
337 valign = attrs[(None, 'valign')]
338 self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)
339
340 def characters(self, chars):
341 pass
342
343
344 def load_session(filename):
345 """Load a Thuban session from the file object file"""
346
347 handler = SessionLoader()
348 handler.read(filename)
349
350 session = handler.theSession
351 # Newly loaded session aren't modified
352 session.UnsetModified()
353
354 return session
355

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26