/[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 874 - (show annotations)
Fri May 9 16:31:12 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: 13345 byte(s)
(XMLReader.encode): New. Encodes the given
        string appropriately for use in Thuban. Fixes RTbug #1851.
(SessionLoader.end_projection): Handle the context of the
        projection tag a bit better by looking at what objects are not
        None. There was an assumption that a projection tag for a map
        could occur before any layers.
(SessionLoader.start_clrange): Provide backward compatibility for
        reading min/max values as well as the new range parameter.

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
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 'classification': ("start_classification", "end_classification"),
196 'clnull' : ("start_clnull", "end_clnull"),
197 'clpoint' : ("start_clpoint", "end_clpoint"),
198 'clrange' : ("start_clrange", "end_clrange"),
199 'cldata' : ("start_cldata", "end_cldata"),
200 'table' : ("start_table", "end_table"),
201 'labellayer' : ("start_labellayer", None),
202 'label' : ("start_label", None)})
203
204 def start_session(self, name, qname, attrs):
205 self.theSession = Session(self.encode(attrs.get((None, 'title'), None)))
206
207 def end_session(self, name, qname):
208 pass
209
210 def start_map(self, name, qname, attrs):
211 """Start a map."""
212 self.aMap = Map(attrs.get((None, 'title'), None))
213
214 def end_map(self, name, qname):
215 self.theSession.AddMap(self.aMap)
216 self.aMap = None
217
218 def start_projection(self, name, qname, attrs):
219 self.ProjectionName = self.encode(attrs.get((None, 'name'), None))
220 self.ProjectionParams = [ ]
221
222 def end_projection(self, name, qname):
223 if self.aLayer is not None:
224 obj = self.aLayer
225 elif self.aMap is not None:
226 obj = self.aMap
227 else:
228 assert False, "projection tag out of context"
229 pass
230
231 obj.SetProjection(
232 Projection(self.ProjectionParams, self.ProjectionName))
233
234 def start_parameter(self, name, qname, attrs):
235 s = attrs.get((None, 'value'))
236 s = str(s) # we can't handle unicode in proj
237 self.ProjectionParams.append(s)
238
239 def start_layer(self, name, qname, attrs, layer_class = Layer):
240 """Start a layer
241
242 Instantiate a layer of class layer_class from the attributes in
243 attrs which may be a dictionary as well as the normal SAX attrs
244 object and bind it to self.aLayer.
245 """
246 title = self.encode(attrs.get((None, 'title'), ""))
247 filename = attrs.get((None, 'filename'), "")
248 filename = os.path.join(self.GetDirectory(), filename)
249 visible = attrs.get((None, 'visible'), "true")
250 fill = parse_color(attrs.get((None, 'fill'), "None"))
251 stroke = parse_color(attrs.get((None, 'stroke'), "#000000"))
252 stroke_width = int(attrs.get((None, 'stroke_width'), "1"))
253 self.aLayer = layer_class(title,
254 self.theSession.OpenShapefile(filename),
255 fill = fill, stroke = stroke,
256 lineWidth = stroke_width,
257 visible = visible != "false")
258
259 def end_layer(self, name, qname):
260 self.aMap.AddLayer(self.aLayer)
261 self.aLayer = None
262
263 def start_classification(self, name, qname, attrs):
264 field = attrs.get((None, 'field'), None)
265
266 fieldType = attrs.get((None, 'field_type'), None)
267 dbFieldType = self.aLayer.GetFieldType(field)
268
269 if fieldType != dbFieldType:
270 raise ValueError(_("xml field type differs from database!"))
271
272 # setup conversion routines depending on the kind of data
273 # we will be seeing later on
274 if fieldType == FIELDTYPE_STRING:
275 self.conv = str
276 elif fieldType == FIELDTYPE_INT:
277 self.conv = lambda p: int(float(p))
278 elif fieldType == FIELDTYPE_DOUBLE:
279 self.conv = float
280
281 self.aLayer.GetClassification().SetField(field)
282
283 def end_classification(self, name, qname):
284 pass
285
286 def start_clnull(self, name, qname, attrs):
287 self.cl_group = ClassGroupDefault()
288 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
289 self.cl_prop = ClassGroupProperties()
290
291 def end_clnull(self, name, qname):
292 self.cl_group.SetProperties(self.cl_prop)
293 self.aLayer.GetClassification().SetDefaultGroup(self.cl_group)
294 del self.cl_group, self.cl_prop
295
296 def start_clpoint(self, name, qname, attrs):
297 attrib_value = attrs.get((None, 'value'), "0")
298
299 value = self.conv(attrib_value)
300
301 self.cl_group = ClassGroupSingleton(value)
302 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
303 self.cl_prop = ClassGroupProperties()
304
305
306 def end_clpoint(self, name, qname):
307 self.cl_group.SetProperties(self.cl_prop)
308 self.aLayer.GetClassification().AppendGroup(self.cl_group)
309 del self.cl_group, self.cl_prop
310
311 def start_clrange(self, name, qname, attrs):
312
313 range = attrs.get((None, 'range'), None)
314 # for backward compatibility (min/max are not saved)
315 min = attrs.get((None, 'min'), None)
316 max = attrs.get((None, 'max'), None)
317
318 try:
319 if range is not None:
320 self.cl_group = ClassGroupRange(Range(range))
321 elif min is not None and max is not None:
322 self.cl_group = ClassGroupRange(self.conv(min), self.conv(max))
323 else:
324 self.cl_group = ClassGroupRange(Range(None))
325
326 except ValueError:
327 raise ValueError(_("Classification range is not a number!"))
328
329 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
330 self.cl_prop = ClassGroupProperties()
331
332
333 def end_clrange(self, name, qname):
334 self.cl_group.SetProperties(self.cl_prop)
335 self.aLayer.GetClassification().AppendGroup(self.cl_group)
336 del self.cl_group, self.cl_prop
337
338 def start_cldata(self, name, qname, attrs):
339 self.cl_prop.SetLineColor(
340 parse_color(attrs.get((None, 'stroke'), "None")))
341 self.cl_prop.SetLineWidth(
342 int(attrs.get((None, 'stroke_width'), "0")))
343 self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
344
345 def end_cldata(self, name, qname):
346 pass
347
348 def start_table(self, name, qname, attrs):
349 #print "table title: %s" % attrs.get('title', None)
350 pass
351
352 def end_table(self, name, qname):
353 pass
354
355 def start_labellayer(self, name, qname, attrs):
356 self.aLayer = self.aMap.LabelLayer()
357
358 def start_label(self, name, qname, attrs):
359 x = float(attrs[(None, 'x')])
360 y = float(attrs[(None, 'y')])
361 text = self.encode(attrs[(None, 'text')])
362 halign = attrs[(None, 'halign')]
363 valign = attrs[(None, 'valign')]
364 self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)
365
366 def characters(self, chars):
367 pass
368
369
370 def load_session(filename):
371 """Load a Thuban session from the file object file"""
372
373 handler = SessionLoader()
374 handler.read(filename)
375
376 session = handler.theSession
377 # Newly loaded session aren't modified
378 session.UnsetModified()
379
380 return session
381

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26