/[thuban]/branches/WIP-pyshapelib-bramz/test/test_load.py
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/test/test_load.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1268 - (show annotations)
Fri Jun 20 16:10:12 2003 UTC (21 years, 8 months ago) by bh
Original Path: trunk/thuban/test/test_load.py
File MIME type: text/x-python
File size: 20507 byte(s)
* Resources/XML/thuban-0.8.dtd: New DTD for the new file format
version.

* Thuban/Model/save.py (sort_data_stores): New. Make topological
sort of the data sources so they can be written with sources that
data sources that depend on other data sources come after the
sources they depend on.
(SessionSaver.__init__): Add idmap instance variable to map from
objects to the ids used in the file.
(SessionSaver.get_id, SessionSaver.define_id)
(SessionSaver.has_id): New. Methods to manage the idmap
(SessionSaver.write): Use thuban-0.8.dtd
(SessionSaver.write_session): Add a namespace on the session and
write out the data sources before the maps.
(SessionSaver.write_data_containers): New. Write the data
containers.
(SessionSaver.write_layer): Layer elements now refer to a
shapestore and don't contain filenames anymore.

* Thuban/Model/load.py (LoadError): Exception class to raise when
errors in the files are discovered
(SessionLoader.__init__): Define dispatchers for elements with a
thuban-0.8 namespace too.
(SessionLoader.check_attrs): New helper method to check and
convert attributes
(AttrDesc): New. Helper class for SessionLoader.check_attrs
(SessionLoader.start_fileshapesource)
(SessionLoader.start_derivedshapesource)
(SessionLoader.start_filetable, SessionLoader.start_jointable):
Handlers for the new elements in the new fileformat
(SessionLoader.start_layer): Handle the shapestore attribute in
addition to filename.
(SessionLoader.start_table, SessionLoader.end_table): Removed.
They were never used in the old formats and aren't needed for the
new.

* Thuban/Model/session.py (Session.DataContainers): New method to
return all "data containers", i.e. shapestores and tables

* test/xmlsupport.py (SaxEventLister.__init__)
(SaxEventLister.startElementNS, sax_eventlist): Add support to
normalize IDs.

* test/test_xmlsupport.py
(TestEventList.test_even_list_id_normalization): New test case for
id normalization

* test/test_load.py (LoadSessionTest.check_format): Use ID
normalization
(LoadSessionTest.thubanids, LoadSessionTest.thubanidrefs): New
class atrributes used for ID normalization
(TestSingleLayer, TestLayerVisibility, TestLabels.test)
(TestLayerProjection.test, TestRasterLayer.test): Adapt to new
file format
(TestJoinedTable): New test for loading sessions with joined
tables.
(TestLoadError): New. Test whether missing required attributes
cause a LoadError.

* test/test_save.py (SaveSessionTest.thubanids)
(SaveSessionTest.thubanidrefs): New class attributes for ID
normalization in .thuban files.
(SaveSessionTest.compare_xml): Use id-normalization.
(SaveSessionTest.testEmptySession)
(SaveSessionTest.testLayerProjection)
(SaveSessionTest.testRasterLayer)
(SaveSessionTest.testClassifiedLayer): Adapt to new file format.
(SaveSessionTest.testLayerProjection): The filename used was the
same as for testSingleLayer. Use a different one.
(SaveSessionTest.test_dbf_table)
(SaveSessionTest.test_joined_table): New test cases for saving the
new data sources structures.
(TestStoreSort, MockDataStore): Classes to test the sorting of the
data stores for writing.

1 # Copyright (c) 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Bernhard Herzog <[email protected]>
4 #
5 # This program is free software under the GPL (>=v2)
6 # Read the file COPYING coming with Thuban for details.
7
8 """
9 Test loading a thuban session from a file
10
11 The tests in this file (test_load.py) are always be the tests for the
12 current version of the thuban file format. Tests for older versions can
13 be found in the version specific test modules, e.g. test_load_0_2 for
14 files created by Thuban 0.2.
15
16 Maintenance of the test cases:
17
18 When during a development period the file format is changed with respect
19 to the last released version for the first, the tests here should be
20 copied to the version specific test file. The round-trip tests which
21 save the session again and compare the XML files should not be copied
22 over as they only make sense here to make sure th that the files checked
23 here are actually ones that may have been written by the current thuban
24 version.
25 """
26
27 __version__ = "$Revision$"
28 # $Source$
29 # $Id$
30
31 import os
32 import unittest
33
34 import support
35 support.initthuban()
36
37 from xmlsupport import sax_eventlist
38
39 import dbflib
40
41 from Thuban.Model.save import save_session
42 from Thuban.Model.load import load_session, parse_color, LoadError
43 from Thuban.Model.color import Color
44 from Thuban.Model.classification import ClassGroupProperties, ClassGroupRange,\
45 ClassGroupSingleton, ClassGroupDefault
46
47
48 def filenames_equal(name1, name2):
49 """Return true if the filenames name1 and name2 are equal.
50
51 On systems where it is available, simply use os.path.samefile,
52 otherwise return whether the normalized versions of the filenames
53 according to os.path.normpath are equal.
54 """
55 if hasattr(os.path, "samefile"):
56 return os.path.samefile(name1, name2)
57 return os.path.normpath(name1) == os.path.normpath(name2)
58
59
60
61 class LoadSessionTest(support.FileLoadTestCase):
62
63 """Base class for .thuban file loading tests
64
65 Basically the same as the FileLoadTestCase, except that all tests
66 use the '.thuban' extension by default and that setUp and tearDown
67 handle sessions.
68 """
69
70 file_extension = ".thuban"
71
72 def setUp(self):
73 """Create the test files"""
74 support.FileLoadTestCase.setUp(self)
75 self.session = None
76
77 def tearDown(self):
78 if self.session is not None:
79 self.session.Destroy()
80 self.session = None
81
82
83 dtd = "http://thuban.intevation.org/dtds/thuban-0.8.dtd"
84 thubanids = [((dtd, n), (None, "id")) for n in
85 ["fileshapesource", "filetable", "jointable",
86 "derivedshapesource"]]
87 thubanidrefs = [((dtd, n), (None, m)) for n, m in
88 [("layer", "shapestore"),
89 ("jointable", "left"),
90 ("jointable", "right"),
91 ("derivedshapesource", "table"),
92 ("derivedshapesource", "shapesource")]]
93 del n, m, dtd
94
95 def check_format(self):
96 """Check whether the file we loaded from matches the one that
97 would be written. Call this from each test case after loading
98 the session
99 """
100 filename = self.temp_file_name(self.id() + ".roundtrip.thuban")
101 save_session(self.session, filename)
102 el1 = sax_eventlist(filename = filename, ids = self.thubanids,
103 idrefs = self.thubanidrefs)
104 el2 = sax_eventlist(filename = self.filename(), ids = self.thubanids,
105 idrefs = self.thubanidrefs)
106 if 0:
107 for a, b in zip(el1, el2):
108 print a != b and "***************" or ""
109 print a
110 print b
111 self.assertEquals(el1, el2,
112 "loaded file not equivalent to the saved file")
113
114
115 class ClassificationTest(LoadSessionTest):
116
117 """
118 Base class for tests that do some detailed checking of classifications
119 """
120
121 def TestLayers(self, layers, expected):
122 TITLE = 0
123 NUM_GROUPS = 1
124 CLASSES = 2
125 GROUP_TYPE = 0
126 GROUP_DATA = 1
127 GROUP_LABEL = 2
128 GROUP_PROPS = 3
129
130 eq = self.assertEquals
131
132 eq(len(layers), len(expected))
133
134 for layer, data in zip(layers, expected):
135 eq(layer.Title(), data[TITLE])
136
137 clazz = layer.GetClassification()
138 eq(clazz.GetNumGroups(), data[NUM_GROUPS])
139 eq(clazz.GetNumGroups() + 1, len(data[CLASSES]))
140
141 i = 0
142 for group in clazz:
143 props = ClassGroupProperties()
144 props.SetLineColor(
145 parse_color(data[CLASSES][i][GROUP_PROPS][0]))
146 props.SetLineWidth(data[CLASSES][i][GROUP_PROPS][1])
147 props.SetFill(
148 parse_color(data[CLASSES][i][GROUP_PROPS][2]))
149
150 if data[CLASSES][i][GROUP_TYPE] == "default":
151 g = ClassGroupDefault(props, data[CLASSES][i][GROUP_LABEL])
152 elif data[CLASSES][i][GROUP_TYPE] == "range":
153 g = ClassGroupRange(data[CLASSES][i][GROUP_DATA][0],
154 data[CLASSES][i][GROUP_DATA][1],
155 props, data[CLASSES][i][GROUP_LABEL])
156 elif data[CLASSES][i][GROUP_TYPE] == "single":
157 g = ClassGroupSingleton(data[CLASSES][i][GROUP_DATA],
158 props, data[CLASSES][i][GROUP_LABEL])
159
160 eq(group, g)
161
162 i += 1
163
164
165
166 class TestSingleLayer(LoadSessionTest):
167
168 file_contents = '''\
169 <?xml version="1.0" encoding="UTF-8"?>
170 <!DOCTYPE session SYSTEM "thuban-0.8.dtd">
171 <session xmlns="http://thuban.intevation.org/dtds/thuban-0.8.dtd"
172 title="single map&amp;layer">
173 <fileshapesource filetype="shapefile" id="D1"
174 filename="../../Data/iceland/political.shp"/>
175 <map title="Test Map">
176 <projection name="Unknown">
177 <parameter value="zone=26"/>
178 <parameter value="proj=utm"/>
179 <parameter value="ellps=clrk66"/>
180 </projection>
181 <layer shapestore="D1" visible="true"
182 stroke="#000000" title="My Layer" stroke_width="1"
183 fill="None"/>
184 </map>
185 </session>
186 '''
187
188 def test(self):
189 """Load a session with a single map with a single layer"""
190 eq = self.assertEquals
191 session = load_session(self.filename())
192 self.session = session
193
194 # Check the title
195 eq(session.Title(), "single map&layer")
196
197 # the session has one map.
198 maps = session.Maps()
199 eq(len(maps), 1)
200
201 # Check the map's attributes
202 map = maps[0]
203 eq(map.Title(), "Test Map")
204
205 # the map has a single layer
206 layers = map.Layers()
207 eq(len(layers), 1)
208
209 # Check the layer attributes
210 layer = layers[0]
211 eq(layer.Title(), "My Layer")
212 self.failUnless(filenames_equal(layer.ShapeStore().FileName(),
213 os.path.join(self.temp_dir(),
214 os.pardir, os.pardir,
215 "Data", "iceland",
216 "political.shp")))
217 eq(layer.GetClassification().GetDefaultFill(), Color.Transparent)
218 eq(layer.GetClassification().GetDefaultLineColor().hex(), "#000000")
219 eq(layer.Visible(), True)
220
221 self.check_format()
222
223 self.session.Destroy()
224 self.session = None
225
226
227 class TestLayerVisibility(LoadSessionTest):
228
229 file_contents = '''\
230 <?xml version="1.0" encoding="UTF-8"?>
231 <!DOCTYPE session SYSTEM "thuban-0.8.dtd">
232 <session xmlns="http://thuban.intevation.org/dtds/thuban-0.8.dtd"
233 title="single map&amp;layer">
234 <fileshapesource filetype="shapefile" id="D1"
235 filename="../../Data/iceland/political.shp"/>
236 <map title="Test Map">
237 <projection name="Unknown">
238 <parameter value="zone=26"/>
239 <parameter value="proj=utm"/>
240 <parameter value="ellps=clrk66"/>
241 </projection>
242 <layer shapestore="D1" visible="false" stroke="#000000"
243 title="My Layer" stroke_width="1" fill="None"/>
244 </map>
245 </session>
246 '''
247
248 def test(self):
249 """Test that the visible flag is correctly loaded for a layer."""
250 eq = self.assertEquals
251 session = load_session(self.filename())
252 self.session = session
253 maps = session.Maps()
254 eq(len(maps), 1)
255 map = maps[0]
256 layers = map.Layers()
257 eq(len(layers), 1)
258 layer = layers[0]
259
260 eq(layer.Visible(), False)
261
262 self.check_format()
263
264
265 class TestClassification(ClassificationTest):
266
267 file_contents = '''\
268 <?xml version="1.0" encoding="UTF-8"?>
269 <!DOCTYPE session SYSTEM "thuban.dtd">
270 <session title="single map&amp;layer">
271 <map title="Test Map">
272 <projection>
273 <parameter value="zone=26"/>
274 <parameter value="proj=utm"/>
275 <parameter value="ellps=clrk66"/>
276 </projection>
277 <layer title="My Layer" stroke_width="1" fill="None"
278 filename="../../Data/iceland/political.shp"
279 stroke="#000000">
280 <classification field="POPYREG" field_type="string">
281 <clnull>
282 <cldata stroke="#000000" stroke_width="1" fill="None"/>
283 </clnull>
284 <clpoint value="1">
285 <cldata stroke="#000000" stroke_width="2" fill="None"/>
286 </clpoint>
287 <clpoint value="1">
288 <cldata stroke="#000000" stroke_width="10" fill="None"/>
289 </clpoint>
290 </classification>
291 </layer>
292 <layer title="My Layer 2" stroke_width="1" fill="None"
293 filename="../../Data/iceland/political.shp"
294 stroke="#000000">
295 <classification field="AREA" field_type="double">
296 <clnull>
297 <cldata stroke="#000000" stroke_width="2" fill="None"/>
298 </clnull>
299 <clrange min="0" max="1">
300 <cldata stroke="#111111" stroke_width="1" fill="None"/>
301 </clrange>
302 <clpoint value=".5">
303 <cldata stroke="#000000" stroke_width="1" fill="#111111"/>
304 </clpoint>
305 <clrange min="-1" max="0">
306 <cldata stroke="#000000" stroke_width="1" fill="None"/>
307 </clrange>
308 <clpoint value="-.5">
309 <cldata stroke="#000000" stroke_width="1" fill="None"/>
310 </clpoint>
311 </classification>
312 </layer>
313 </map>
314 </session>
315 '''
316
317 def test(self):
318 """Load a Thuban session with a map and classified layers."""
319 session = load_session(self.filename())
320 self.session = session
321
322 map = self.session.Maps()[0] # only one map in the sample
323
324 expected = [("My Layer", 2,
325 [("default", (), "",
326 ("#000000", 1, "None")),
327 ("single", "1", "",
328 ("#000000", 2, "None")),
329 ("single", "1", "",
330 ("#000000", 10, "None"))]),
331 ("My Layer 2", 4,
332 [("default", (), "",
333 ("#000000", 2, "None")),
334 ("range", (0, 1), "",
335 ("#111111", 1, "None")),
336 ("single", .5, "",
337 ("#000000", 1, "#111111")),
338 ("range", (-1, 0), "",
339 ("#000000", 1, "None")),
340 ("single", -.5, "",
341 ("#000000", 1, "None"))])]
342
343 self.TestLayers(map.Layers(), expected)
344
345
346 class TestLabels(ClassificationTest):
347
348 file_contents = '''\
349 <?xml version="1.0" encoding="UTF-8"?>
350 <!DOCTYPE session SYSTEM "thuban-0.8.dtd">
351 <session xmlns="http://thuban.intevation.org/dtds/thuban-0.8.dtd"
352 title="single map&amp;layer">
353 <fileshapesource filetype="shapefile" id="D1"
354 filename="../../Data/iceland/political.shp"/>
355 <map title="Test Map">
356 <projection name="Unknown">
357 <parameter value="zone=26"/>
358 <parameter value="proj=utm"/>
359 <parameter value="ellps=clrk66"/>
360 </projection>
361 <layer shapestore="D1" visible="true" stroke="#000000"
362 title="My Layer" stroke_width="1" fill="None">
363 <classification field="POPYREG" field_type="string">
364 <clnull label="hallo">
365 <cldata stroke="#000000" stroke_width="1" fill="None"/>
366 </clnull>
367 <clpoint label="welt" value="1">
368 <cldata stroke="#000000" stroke_width="2" fill="None"/>
369 </clpoint>
370 </classification>
371 </layer>
372 </map>
373 </session>
374 '''
375
376 def test(self):
377 """Load a session and test for reading the group labels."""
378 eq = self.assertEquals
379 session = load_session(self.filename())
380 self.session = session
381
382 map = self.session.Maps()[0] # only one map in the sample
383
384 expected = [("My Layer", 1,
385 [("default", (), "hallo",
386 ("#000000", 1, "None")),
387 ("single", "1", "welt",
388 ("#000000", 2, "None"))])]
389
390 self.TestLayers(map.Layers(), expected)
391 self.check_format()
392
393
394 class TestLayerProjection(LoadSessionTest):
395
396 file_contents = '''\
397 <?xml version="1.0" encoding="UTF-8"?>
398 <!DOCTYPE session SYSTEM "thuban-0.8.dtd">
399 <session xmlns="http://thuban.intevation.org/dtds/thuban-0.8.dtd"
400 title="single map&amp;layer">
401 <fileshapesource filetype="shapefile" id="D2"
402 filename="../../Data/iceland/roads-line.shp"/>
403 <fileshapesource filetype="shapefile" id="D4"
404 filename="../../Data/iceland/political.shp"/>
405 <map title="Test Map">
406 <projection name="Unknown">
407 <parameter value="zone=26"/>
408 <parameter value="proj=utm"/>
409 <parameter value="ellps=clrk66"/>
410 </projection>
411 <layer shapestore="D4" visible="true" stroke="#000000"
412 title="My Layer" stroke_width="1" fill="None">
413 <projection name="hello">
414 <parameter value="zone=13"/>
415 <parameter value="proj=tmerc"/>
416 <parameter value="ellps=clrk66"/>
417 </projection>
418 <classification field="POPYREG" field_type="string">
419 <clnull label="hallo">
420 <cldata stroke="#000000" stroke_width="1" fill="None"/>
421 </clnull>
422 <clpoint label="welt" value="1">
423 <cldata stroke="#000000" stroke_width="2" fill="None"/>
424 </clpoint>
425 </classification>
426 </layer>
427 <layer shapestore="D2" visible="true" stroke="#000000"
428 title="My Layer" stroke_width="1" fill="None">
429 <projection name="Unknown">
430 <parameter value="proj=lcc"/>
431 <parameter value="ellps=clrk66"/>
432 </projection>
433 </layer>
434 </map>
435 </session>
436 '''
437
438 def test(self):
439 """Test loading layers with projections"""
440 eq = self.assertEquals
441 neq = self.assertNotEqual
442
443 session = load_session(self.filename())
444 self.session = session
445
446 map = self.session.Maps()[0] # only one map in the sample
447
448 layers = map.Layers() # two layers in the sample
449
450 # test layer with a named projection
451 proj = layers[0].GetProjection()
452 neq(proj, None)
453 eq(proj.GetName(), "hello")
454 eq(proj.GetParameter("proj"), "tmerc")
455 eq(proj.GetParameter("zone"), "13")
456 eq(proj.GetParameter("ellps"), "clrk66")
457
458 # test layer with an unnamed projection
459 proj = layers[1].GetProjection()
460 neq(proj, None)
461 eq(proj.GetName(), "Unknown")
462 eq(proj.GetParameter("proj"), "lcc")
463 eq(proj.GetParameter("ellps"), "clrk66")
464
465 self.check_format()
466
467
468 class TestRasterLayer(LoadSessionTest):
469
470 file_contents = '''\
471 <?xml version="1.0" encoding="UTF-8"?>
472 <!DOCTYPE session SYSTEM "thuban-0.8.dtd">
473 <session xmlns="http://thuban.intevation.org/dtds/thuban-0.8.dtd"
474 title="single map&amp;layer">
475 <map title="Test Map">
476 <rasterlayer visible="false" filename="../../Data/iceland/island.tif"
477 title="My RasterLayer"/>
478 </map>
479 </session>
480 '''
481
482 def test(self):
483 eq = self.assertEquals
484 neq = self.assertNotEqual
485
486 session = load_session(self.filename())
487 self.session = session
488
489 map = self.session.Maps()[0] # only one map in the sample
490
491 layer = map.Layers()[0] # one layer in the sample
492
493 eq(layer.Title(), "My RasterLayer")
494 self.failIf(layer.Visible())
495 self.failUnless(filenames_equal(layer.GetImageFilename(),
496 os.path.join(self.temp_dir(),
497 os.pardir, os.pardir,
498 "Data", "iceland",
499 "island.tif")))
500 self.check_format()
501
502
503 class TestJoinedTable(LoadSessionTest):
504
505 file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
506 <!DOCTYPE session SYSTEM "thuban-0.8.dtd">
507 <session title="A Joined Table session">
508 <filetable filename="load_joinedtable.dbf"
509 title="Some Title"
510 filetype="DBF" id="D141881756"/>
511 <fileshapesource filename="../../Data/iceland/roads-line.shp"
512 filetype="shapefile" id="D142197204"/>
513 <jointable id="D142180284"
514 title="Joined"
515 leftcolumn="RDLNTYPE" left="D142197204"
516 rightcolumn="RDTYPE" right="D141881756"/>
517 <derivedshapesource id="D141915644"
518 table="D142180284" shapesource="D142197204"/>
519 <map title="Test Map">
520 <layer title="My Layer"
521 shapestore="D141915644" visible="true"
522 stroke="#000000" stroke_width="1" fill="None"/>
523 </map>
524 </session>'''
525
526 def setUp(self):
527 """Extend inherited method to create the dbffile for the join"""
528 LoadSessionTest.setUp(self)
529 dbffile = self.temp_file_name("load_joinedtable.dbf")
530 dbf = dbflib.create(dbffile)
531 dbf.add_field("RDTYPE", dbflib.FTInteger, 10, 0)
532 dbf.add_field("TEXT", dbflib.FTString, 10, 0)
533 dbf.write_record(0, {'RDTYPE': 8, "TEXT": "foo"})
534 dbf.write_record(1, {'RDTYPE': 2, "TEXT": "bar"})
535 dbf.write_record(2, {'RDTYPE': 3, "TEXT": "baz"})
536 dbf.close()
537
538 def test(self):
539 """Test loading a session containing a joined table"""
540 session = load_session(self.filename())
541 self.session = session
542
543 tables = session.Tables()
544 self.assertEquals(len(tables), 3)
545 # FIXME: The tests shouldn't assume a certain order of the tables
546 self.assertEquals(tables[0].Title(), "Some Title")
547 self.assertEquals(tables[1].Title(), "Joined")
548
549
550 class TestLoadError(LoadSessionTest):
551
552 file_contents = '''\
553 <?xml version="1.0" encoding="UTF-8"?>
554 <!DOCTYPE session SYSTEM "thuban-0.8.dtd">
555 <session xmlns="http://thuban.intevation.org/dtds/thuban-0.8.dtd"
556 title="single map&amp;layer">
557 <fileshapesource id="D1" filename="../../Data/iceland/political.shp"/>
558 <map title="Test Map">
559 <projection name="Unknown">
560 <parameter value="zone=26"/>
561 <parameter value="proj=utm"/>
562 <parameter value="ellps=clrk66"/>
563 </projection>
564 <layer shapestore="D1" visible="true"
565 stroke="#000000" title="My Layer" stroke_width="1"
566 fill="None"/>
567 </map>
568 </session>
569 '''
570
571 def test(self):
572 """Test loading a session missing a required attribute"""
573 # Don't use assertRaises to make sure that if a session is
574 # actually returned it gets destroyed properly.
575 try:
576 self.session = load_session(self.filename())
577 except LoadError, value:
578 pass
579 else:
580 self.fail("Missing filetype attribute doesn't raise LoadError")
581
582 if __name__ == "__main__":
583 unittest.main()

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26