/[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 723 - (show annotations)
Thu Apr 24 15:31:53 2003 UTC (21 years, 10 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/load.py
File MIME type: text/x-python
File size: 12600 byte(s)
First step towards table management. Introduce a simple data
abstraction so that we replace the data a layer uses more easily
in the next step.

* Thuban/Model/data.py: New file with a simple data abstraction
that bundles shapefile and dbffile into one object.

* Thuban/Model/session.py (Session.OpenShapefile): New method to
open shapefiles and return a shape store object

* Thuban/Model/layer.py (Layer.__init__): Pass the data as a store
object instead of a shapefile filename. This introduces a new
instance variable store holding the datastore. For intermediate
backwards compatibility keep the old instance variables.
(open_shapefile): Removed. No longer needed with the shape store.
(Layer.SetShapeStore, Layer.ShapeStore): New methods to set and
get the shape store used by a layer.
(Layer.Destroy): No need to explicitly destroy the shapefile or
table anymore.

* Thuban/UI/mainwindow.py (MainWindow.AddLayer)
(MainWindow.AddLayer): Use the session's OpenShapefile method to
open shapefiles

* Thuban/Model/load.py (ProcessSession.start_layer): Use the
session's OpenShapefile method to open shapefiles

* test/test_classification.py
(TestClassification.test_classification): Use the session's
OpenShapefile method to open shapefiles and build the filename in
a more platform independed way

* test/test_layer.py (TestLayer.setUp, TestLayer.tearDown):
Implement to have a session to use in the tests
(TestLayer.test_arc_layer, TestLayer.test_polygon_layer)
(TestLayer.test_point_layer, TestLayer.test_empty_layer): Use the
session's OpenShapefile method to open shapefiles
(TestLayerLegend.setUp): Instantiate a session so that we can use
it to open shapefiles.
(TestLayerLegend.tearDown): Make sure that all references to
layers and session are removed otherwise we may get a resource
leak

* test/test_map.py (TestMapAddLayer.test_add_layer)
(TestMapWithContents.setUp): Instantiate a session so that we can
use it to open shapefiles.
(TestMapWithContents.tearDown): Make sure that all references to
layers, maps and sessions are removed otherwise we may get a
resource leak
("__main__"): use support.run_tests() so that more info about
uncollected garbage is printed

* test/test_save.py (SaveSessionTest.testSingleLayer): Use the
session's OpenShapefile method to open shapefiles
("__main__"): use support.run_tests() so that more info about
uncollected garbage is printed

* test/test_selection.py (TestSelection.tearDown): Make sure that
all references to the session and the selection are removed
otherwise we may get a resource leak
(TestSelection.get_layer): Instantiate a session so that we can
use it to open shapefiles.
("__main__"): use support.run_tests() so that more info about
uncollected garbage is printed

* test/test_session.py (TestSessionBase.tearDown)
(TestSessionWithContent.tearDown): Make sure that all references
to the session and layers are removed otherwise we may get a
resource leak
(TestSessionWithContent.setUp): Use the session's OpenShapefile
method to open shapefiles

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 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,
233 self.theSession.OpenShapefile(filename),
234 fill = fill, stroke = stroke,
235 lineWidth = stroke_width)
236
237 def end_layer(self, name, qname):
238 self.aMap.AddLayer(self.aLayer)
239
240 def start_classification(self, name, qname, attrs):
241 field = attrs.get((None, 'field'), None)
242
243 fieldType = attrs.get((None, 'field_type'), None)
244 dbFieldType = self.aLayer.GetFieldType(field)
245
246 if fieldType != dbFieldType:
247 raise ValueError(_("xml field type differs from database!"))
248
249 # setup conversion routines depending on the kind of data
250 # we will be seeing later on
251 if fieldType == FIELDTYPE_STRING:
252 self.conv = str
253 elif fieldType == FIELDTYPE_INT:
254 self.conv = lambda p: int(float(p))
255 elif fieldType == FIELDTYPE_DOUBLE:
256 self.conv = float
257
258 self.aLayer.GetClassification().SetField(field)
259
260
261 def end_classification(self, name, qname):
262 pass
263
264 def start_clnull(self, name, qname, attrs):
265 self.cl_group = ClassGroupDefault()
266 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
267 self.cl_prop = ClassGroupProperties()
268
269 def end_clnull(self, name, qname):
270 self.cl_group.SetProperties(self.cl_prop)
271 self.aLayer.GetClassification().SetDefaultGroup(self.cl_group)
272 del self.cl_group, self.cl_prop
273
274 def start_clpoint(self, name, qname, attrs):
275 attrib_value = attrs.get((None, 'value'), "0")
276
277 #try:
278 #value = Str2Num(attrib_value)
279 #except:
280 #value = attrib_value
281
282 value = self.conv(attrib_value)
283
284 self.cl_group = ClassGroupSingleton(value)
285 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
286 self.cl_prop = ClassGroupProperties()
287
288
289 def end_clpoint(self, name, qname):
290 self.cl_group.SetProperties(self.cl_prop)
291 self.aLayer.GetClassification().AppendGroup(self.cl_group)
292 del self.cl_group, self.cl_prop
293
294 def start_clrange(self, name, qname, attrs):
295
296 try:
297 min = self.conv(attrs.get((None, 'min'), "0"))
298 max = self.conv(attrs.get((None, 'max'), "0"))
299 #min = Str2Num(attrs.get((None, 'min'), "0"))
300 #max = Str2Num(attrs.get((None, 'max'), "0"))
301 except ValueError:
302 raise ValueError(_("Classification range is not a number!"))
303
304 self.cl_group = ClassGroupRange(min, max)
305 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
306 self.cl_prop = ClassGroupProperties()
307
308
309 def end_clrange(self, name, qname):
310 self.cl_group.SetProperties(self.cl_prop)
311 self.aLayer.GetClassification().AppendGroup(self.cl_group)
312 del self.cl_group, self.cl_prop
313
314 def start_cldata(self, name, qname, attrs):
315 self.cl_prop.SetLineColor(
316 parse_color(attrs.get((None, 'stroke'), "None")))
317 self.cl_prop.SetLineWidth(
318 int(attrs.get((None, 'stroke_width'), "0")))
319 self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
320
321 def end_cldata(self, name, qname):
322 pass
323
324 def start_table(self, name, qname, attrs):
325 #print "table title: %s" % attrs.get('title', None)
326 pass
327
328 def end_table(self, name, qname):
329 pass
330
331 def start_labellayer(self, name, qname, attrs):
332 self.aLayer = self.aMap.LabelLayer()
333
334 def start_label(self, name, qname, attrs):
335 x = float(attrs[(None, 'x')])
336 y = float(attrs[(None, 'y')])
337 text = attrs[(None, 'text')]
338 halign = attrs[(None, 'halign')]
339 valign = attrs[(None, 'valign')]
340 self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)
341
342 def characters(self, chars):
343 pass
344
345
346 def load_session(filename):
347 """Load a Thuban session from the file object file"""
348
349 handler = SessionLoader()
350 handler.read(filename)
351
352 session = handler.theSession
353 # Newly loaded session aren't modified
354 session.UnsetModified()
355
356 return session
357

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26