/[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 930 - (show annotations)
Tue May 20 15:23:03 2003 UTC (21 years, 9 months ago) by jonathan
Original Path: trunk/thuban/Thuban/Model/load.py
File MIME type: text/x-python
File size: 14049 byte(s)
(SessionLoader.__init__): Add rasterlayer tag handler.
(SessionLoader.start_layer): Encode the filename.
(SessionLoader.start_rasterlayer, SessionLoader.end_rasterlayer):
        New. Supports reading a rasterlayer tag.

1 # Copyright (C) 2001, 2002, 2003 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 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
24 from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
25 FIELDTYPE_STRING
26
27 from Thuban.Model.session import Session
28 from Thuban.Model.map import Map
29 from Thuban.Model.layer import Layer, RasterLayer
30 from Thuban.Model.color import Color
31 from Thuban.Model.proj import Projection
32 from Thuban.Model.range import Range
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.__directory = ""
80 self.__dispatchers = {}
81
82 def read(self, file_or_filename):
83
84 if hasattr(file_or_filename, "read"):
85 # it's a file object
86 self.__directory = ""
87 self.__file = file_or_filename
88 else:
89 filename = file_or_filename
90 self.__directory = os.path.dirname(filename)
91 self.__file = open(filename)
92
93 parser = make_parser()
94 parser.setContentHandler(self)
95 parser.setErrorHandler(ErrorHandler())
96 parser.setFeature(xml.sax.handler.feature_namespaces, 1)
97
98 #
99 # Well, this isn't pretty, but it appears that if you
100 # use Python 2.2 without the site-package _xmlplus then
101 # the following will fail, and without them it will work.
102 # However, if you do have the site-package and you don't
103 # call these functions, the reader raises an exception
104 #
105 # The reason we set these to 0 in the first place is
106 # because there is an unresolved issue with external
107 # entities causing an exception in the reader
108 #
109 try:
110 parser.setFeature(xml.sax.handler.feature_validation,0)
111 parser.setFeature(xml.sax.handler.feature_external_ges,0)
112 parser.setFeature(xml.sax.handler.feature_external_pes,0)
113 except SAXNotRecognizedException:
114 pass
115
116 parser.parse(self.__file)
117
118 self.close()
119
120 def close(self):
121 self.__file.close()
122
123 def GetFilename(self):
124 if hasattr(self.__file, "name"):
125 return self.__file.name
126
127 return ""
128
129 def GetDirectory(self):
130 return self.__directory
131
132
133 def AddDispatchers(self, dict):
134 """Add the function names that should be used to process XML tags.
135
136 dict -- a dictionary whose keys are XML tag strings and whose values
137 are pairs of strings such that the first string is
138 the name of the function that should be called when the
139 XML tag opens and the second string is the name of the
140 function that should be called when the XML tag closes.
141 If a pair element is None, no function is called.
142 """
143
144 self.__dispatchers.update(dict)
145
146 def startElementNS(self, name, qname, attrs):
147 """Call the method given for name in self.start_dispatcher
148 """
149 if name[0] is None:
150 method_name = self.__dispatchers.get(name[1])
151 else:
152 # Dispatch with namespace
153 method_name = self.__dispatchers.get(name)
154 if method_name is not None and method_name[0] is not None:
155 getattr(self, method_name[0])(name, qname, attrs)
156
157 def endElementNS(self, name, qname):
158 """Call the method given for name in self.end_dispatcher
159 """
160 if name[0] is None:
161 method_name = self.__dispatchers.get(name[1])
162 else:
163 # Dispatch with namespace
164 method_name = self.__dispatchers.get(name)
165 if method_name is not None and method_name[1] is not None:
166 getattr(self, method_name[1])(name, qname)
167
168 def encode(self, str):
169 """Assume that str is in Unicode and encode it into Latin1.
170
171 If str is None, return None
172 """
173
174 if str is not None:
175 return str.encode("latin1")
176 else:
177 return None
178
179 class SessionLoader(XMLReader):
180
181 def __init__(self):
182 """Inititialize the Sax handler."""
183 XMLReader.__init__(self)
184
185 self.theSession = None
186 self.aMap = None
187 self.aLayer = None
188
189 XMLReader.AddDispatchers(self,
190 {'session' : ("start_session", "end_session"),
191 'map' : ("start_map", "end_map"),
192 'projection' : ("start_projection", "end_projection"),
193 'parameter' : ("start_parameter", None),
194 'layer' : ("start_layer", "end_layer"),
195 'rasterlayer' : ("start_rasterlayer", "end_rasterlayer"),
196 'classification': ("start_classification", "end_classification"),
197 'clnull' : ("start_clnull", "end_clnull"),
198 'clpoint' : ("start_clpoint", "end_clpoint"),
199 'clrange' : ("start_clrange", "end_clrange"),
200 'cldata' : ("start_cldata", "end_cldata"),
201 'table' : ("start_table", "end_table"),
202 'labellayer' : ("start_labellayer", None),
203 'label' : ("start_label", None)})
204
205 def start_session(self, name, qname, attrs):
206 self.theSession = Session(self.encode(attrs.get((None, 'title'), None)))
207
208 def end_session(self, name, qname):
209 pass
210
211 def start_map(self, name, qname, attrs):
212 """Start a map."""
213 self.aMap = Map(attrs.get((None, 'title'), None))
214
215 def end_map(self, name, qname):
216 self.theSession.AddMap(self.aMap)
217 self.aMap = None
218
219 def start_projection(self, name, qname, attrs):
220 self.ProjectionName = self.encode(attrs.get((None, 'name'), None))
221 self.ProjectionParams = [ ]
222
223 def end_projection(self, name, qname):
224 if self.aLayer is not None:
225 obj = self.aLayer
226 elif self.aMap is not None:
227 obj = self.aMap
228 else:
229 assert False, "projection tag out of context"
230 pass
231
232 obj.SetProjection(
233 Projection(self.ProjectionParams, self.ProjectionName))
234
235 def start_parameter(self, name, qname, attrs):
236 s = attrs.get((None, 'value'))
237 s = str(s) # we can't handle unicode in proj
238 self.ProjectionParams.append(s)
239
240 def start_layer(self, name, qname, attrs, layer_class = Layer):
241 """Start a layer
242
243 Instantiate a layer of class layer_class from the attributes in
244 attrs which may be a dictionary as well as the normal SAX attrs
245 object and bind it to self.aLayer.
246 """
247 title = self.encode(attrs.get((None, 'title'), ""))
248 filename = attrs.get((None, 'filename'), "")
249 filename = os.path.join(self.GetDirectory(), filename)
250 filename = self.encode(filename)
251 visible = self.encode(attrs.get((None, 'visible'), "true")) != "false"
252 fill = parse_color(attrs.get((None, 'fill'), "None"))
253 stroke = parse_color(attrs.get((None, 'stroke'), "#000000"))
254 stroke_width = int(attrs.get((None, 'stroke_width'), "1"))
255 self.aLayer = layer_class(title,
256 self.theSession.OpenShapefile(filename),
257 fill = fill, stroke = stroke,
258 lineWidth = stroke_width,
259 visible = visible)
260
261 def end_layer(self, name, qname):
262 self.aMap.AddLayer(self.aLayer)
263 self.aLayer = None
264
265 def start_rasterlayer(self, name, qname, attrs, layer_class = RasterLayer):
266 title = self.encode(attrs.get((None, 'title'), ""))
267 filename = attrs.get((None, 'filename'), "")
268 filename = os.path.join(self.GetDirectory(), filename)
269 filename = self.encode(filename)
270 visible = self.encode(attrs.get((None, 'visible'), "true")) != "false"
271
272 self.aLayer = layer_class(title, filename, visible = visible)
273
274 def end_rasterlayer(self, name, qname):
275 self.aMap.AddLayer(self.aLayer)
276 self.aLayer = None
277
278 def start_classification(self, name, qname, attrs):
279 field = attrs.get((None, 'field'), None)
280
281 fieldType = attrs.get((None, 'field_type'), None)
282 dbFieldType = self.aLayer.GetFieldType(field)
283
284 if fieldType != dbFieldType:
285 raise ValueError(_("xml field type differs from database!"))
286
287 # setup conversion routines depending on the kind of data
288 # we will be seeing later on
289 if fieldType == FIELDTYPE_STRING:
290 self.conv = str
291 elif fieldType == FIELDTYPE_INT:
292 self.conv = lambda p: int(float(p))
293 elif fieldType == FIELDTYPE_DOUBLE:
294 self.conv = float
295
296 self.aLayer.GetClassification().SetField(field)
297
298 def end_classification(self, name, qname):
299 pass
300
301 def start_clnull(self, name, qname, attrs):
302 self.cl_group = ClassGroupDefault()
303 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
304 self.cl_prop = ClassGroupProperties()
305
306 def end_clnull(self, name, qname):
307 self.cl_group.SetProperties(self.cl_prop)
308 self.aLayer.GetClassification().SetDefaultGroup(self.cl_group)
309 del self.cl_group, self.cl_prop
310
311 def start_clpoint(self, name, qname, attrs):
312 attrib_value = attrs.get((None, 'value'), "0")
313
314 value = self.conv(attrib_value)
315
316 self.cl_group = ClassGroupSingleton(value)
317 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
318 self.cl_prop = ClassGroupProperties()
319
320
321 def end_clpoint(self, name, qname):
322 self.cl_group.SetProperties(self.cl_prop)
323 self.aLayer.GetClassification().AppendGroup(self.cl_group)
324 del self.cl_group, self.cl_prop
325
326 def start_clrange(self, name, qname, attrs):
327
328 range = attrs.get((None, 'range'), None)
329 # for backward compatibility (min/max are not saved)
330 min = attrs.get((None, 'min'), None)
331 max = attrs.get((None, 'max'), None)
332
333 try:
334 if range is not None:
335 self.cl_group = ClassGroupRange(Range(range))
336 elif min is not None and max is not None:
337 self.cl_group = ClassGroupRange(self.conv(min), self.conv(max))
338 else:
339 self.cl_group = ClassGroupRange(Range(None))
340
341 except ValueError:
342 raise ValueError(_("Classification range is not a number!"))
343
344 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
345 self.cl_prop = ClassGroupProperties()
346
347
348 def end_clrange(self, name, qname):
349 self.cl_group.SetProperties(self.cl_prop)
350 self.aLayer.GetClassification().AppendGroup(self.cl_group)
351 del self.cl_group, self.cl_prop
352
353 def start_cldata(self, name, qname, attrs):
354 self.cl_prop.SetLineColor(
355 parse_color(attrs.get((None, 'stroke'), "None")))
356 self.cl_prop.SetLineWidth(
357 int(attrs.get((None, 'stroke_width'), "0")))
358 self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
359
360 def end_cldata(self, name, qname):
361 pass
362
363 def start_table(self, name, qname, attrs):
364 #print "table title: %s" % attrs.get('title', None)
365 pass
366
367 def end_table(self, name, qname):
368 pass
369
370 def start_labellayer(self, name, qname, attrs):
371 self.aLayer = self.aMap.LabelLayer()
372
373 def start_label(self, name, qname, attrs):
374 x = float(attrs[(None, 'x')])
375 y = float(attrs[(None, 'y')])
376 text = self.encode(attrs[(None, 'text')])
377 halign = attrs[(None, 'halign')]
378 valign = attrs[(None, 'valign')]
379 self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)
380
381 def characters(self, chars):
382 pass
383
384
385 def load_session(filename):
386 """Load a Thuban session from the file object file"""
387
388 handler = SessionLoader()
389 handler.read(filename)
390
391 session = handler.theSession
392 # Newly loaded session aren't modified
393 session.UnsetModified()
394
395 return session
396

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26