/[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 2734 - (show annotations)
Thu Mar 1 12:42:59 2007 UTC (18 years ago) by bramz
File MIME type: text/x-python
File size: 45160 byte(s)
made a copy
1 # Copyright (c) 2002, 2003, 2004, 2005 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 time, the tests here should
20 be 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 import postgissupport
38 from xmlsupport import sax_eventlist
39
40 import dbflib
41 import shapelib
42
43 from Thuban import internal_from_unicode
44 from Thuban.Model.save import save_session
45 from Thuban.Model.load import load_session, parse_color, LoadError, \
46 LoadCancelled
47 from Thuban.Model.color import Transparent
48 from Thuban.Model.classification import ClassGroupProperties, ClassGroupRange,\
49 ClassGroupSingleton, ClassGroupPattern, ClassGroupDefault
50 from Thuban.Model.postgisdb import ConnectionError
51 from Thuban.Model.table import DBFTable, MemoryTable, \
52 FIELDTYPE_DOUBLE, FIELDTYPE_INT, FIELDTYPE_STRING, \
53 table_to_dbf
54 from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
55 ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE
56
57 def filenames_equal(name1, name2):
58 """Return true if the filenames name1 and name2 are equal.
59
60 On systems where it is available, simply use os.path.samefile,
61 otherwise return whether the normalized versions of the filenames
62 according to os.path.normpath are equal.
63 """
64 if hasattr(os.path, "samefile"):
65 return os.path.samefile(name1, name2)
66 return os.path.normpath(name1) == os.path.normpath(name2)
67
68
69
70 class LoadSessionTest(support.FileLoadTestCase):
71
72 """Base class for .thuban file loading tests
73
74 Basically the same as the FileLoadTestCase, except that all tests
75 use the '.thuban' extension by default and that setUp and tearDown
76 handle sessions.
77 """
78
79 file_extension = ".thuban"
80
81 def setUp(self):
82 """Create the test files"""
83 support.FileLoadTestCase.setUp(self)
84 self.session = None
85
86 def tearDown(self):
87 if self.session is not None:
88 self.session.Destroy()
89 self.session = None
90
91
92 dtd = "http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
93 thubanids = [((dtd, n), (None, "id")) for n in
94 ["fileshapesource", "filetable", "jointable",
95 "derivedshapesource"]]
96 thubanidrefs = [((dtd, n), (None, m)) for n, m in
97 [("layer", "shapestore"),
98 ("jointable", "left"),
99 ("jointable", "right"),
100 ("derivedshapesource", "table"),
101 ("derivedshapesource", "shapesource")]]
102
103 # The filenames in the tests should be understandable on all
104 # currently supported platforms so filenames is an empty list
105 filenames = []
106
107 del n, m, dtd
108
109 def check_format(self):
110 """Check whether the file we loaded from matches the one that
111 would be written. Call this from each test case after loading
112 the session
113 """
114 filename = self.temp_file_name(self.id() + ".roundtrip.thuban")
115 save_session(self.session, filename)
116 el1 = sax_eventlist(filename = filename, ids = self.thubanids,
117 idrefs = self.thubanidrefs,
118 filenames = self.filenames)
119 el2 = sax_eventlist(filename = self.filename(), ids = self.thubanids,
120 idrefs = self.thubanidrefs,
121 filenames = self.filenames)
122 if 0:
123 for a, b in zip(el1, el2):
124 print a != b and "***************" or ""
125 print a
126 print b
127
128 self.assertEquals(el1, el2,
129 "loaded file not equivalent to the saved file")
130
131
132 class ClassificationTest(LoadSessionTest):
133
134 """
135 Base class for tests that do some detailed checking of classifications
136 """
137
138 def TestLayers(self, layers, expected):
139 TITLE = 0
140 NUM_GROUPS = 1
141 CLASSES = 2
142 GROUP_TYPE = 0
143 GROUP_DATA = 1
144 GROUP_LABEL = 2
145 GROUP_PROPS = 3
146
147 eq = self.assertEquals
148
149 eq(len(layers), len(expected))
150
151 for layer, data in zip(layers, expected):
152 eq(layer.Title(), data[TITLE])
153
154 clazz = layer.GetClassification()
155 eq(clazz.GetNumGroups(), data[NUM_GROUPS])
156 eq(clazz.GetNumGroups() + 1, len(data[CLASSES]))
157
158 i = 0
159 for group in clazz:
160 props = ClassGroupProperties()
161 props.SetLineColor(
162 parse_color(data[CLASSES][i][GROUP_PROPS][0]))
163 props.SetLineWidth(data[CLASSES][i][GROUP_PROPS][1])
164 props.SetFill(
165 parse_color(data[CLASSES][i][GROUP_PROPS][2]))
166 if len(data[CLASSES][i][GROUP_PROPS]) > 3:
167 props.SetSize(data[CLASSES][i][GROUP_PROPS][3])
168
169 if data[CLASSES][i][GROUP_TYPE] == "default":
170 g = ClassGroupDefault(props, data[CLASSES][i][GROUP_LABEL])
171 elif data[CLASSES][i][GROUP_TYPE] == "range":
172 g = ClassGroupRange((data[CLASSES][i][GROUP_DATA][0],
173 data[CLASSES][i][GROUP_DATA][1]),
174 props, data[CLASSES][i][GROUP_LABEL])
175 elif data[CLASSES][i][GROUP_TYPE] == "single":
176 g = ClassGroupSingleton(data[CLASSES][i][GROUP_DATA],
177 props, data[CLASSES][i][GROUP_LABEL])
178 elif data[CLASSES][i][GROUP_TYPE] == "pattern":
179 g = ClassGroupPattern(data[CLASSES][i][GROUP_DATA],
180 props, data[CLASSES][i][GROUP_LABEL])
181
182 eq(group, g)
183
184 i += 1
185
186
187
188 class TestSingleLayer(LoadSessionTest):
189
190 # Note: The use of &amp; and non-ascii characters is deliberate. We
191 # want to test whether the loading code handles that correctly.
192 file_contents = '''\
193 <?xml version="1.0" encoding="UTF-8"?>
194 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
195 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
196 title="Stra\xc3\x9fen &amp; Landmarken">
197 <fileshapesource filetype="shapefile" id="D1"
198 filename="../../Data/iceland/political.shp"/>
199 <map title="\xc3\x9cbersicht">
200 <projection epsg="32627" name="WGS 84 / UTM zone 27N">
201 <parameter value="datum=WGS84"/>
202 <parameter value="ellps=WGS84"/>
203 <parameter value="proj=utm"/>
204 <parameter value="units=m"/>
205 <parameter value="zone=27"/>
206 </projection>
207 <layer shapestore="D1" visible="true" title="K\xc3\xbcste">
208 <classification>
209 <clnull label="">
210 <cldata stroke="#000000" stroke_width="1" fill="None"/>
211 </clnull>
212 </classification>
213 </layer>
214 </map>
215 </session>
216 '''
217
218 def test(self):
219 """Load a session with a single map with a single layer"""
220 eq = self.assertEquals
221 session = load_session(self.filename())
222 self.session = session
223
224 # Check the title
225 eq(session.Title(), internal_from_unicode(u"Stra\xdfen & Landmarken"))
226
227 # the session has one map.
228 maps = session.Maps()
229 eq(len(maps), 1)
230
231 # Check the map's attributes
232 map = maps[0]
233 eq(map.Title(), internal_from_unicode(u"\xdcbersicht"))
234 proj = map.GetProjection()
235 eq(proj.GetName(), "WGS 84 / UTM zone 27N")
236 eq(proj.EPSGCode(), "32627")
237 params = proj.GetAllParameters()
238 params.sort()
239 eq(params, ["datum=WGS84", "ellps=WGS84", "proj=utm", "units=m",
240 "zone=27"])
241
242 # the map has a single layer
243 layers = map.Layers()
244 eq(len(layers), 1)
245
246 # Check the layer attributes
247 layer = layers[0]
248 eq(layer.Title(), internal_from_unicode(u"K\xfcste"))
249 self.failUnless(filenames_equal(layer.ShapeStore().FileName(),
250 os.path.join(self.temp_dir(),
251 os.pardir, os.pardir,
252 "Data", "iceland",
253 "political.shp")))
254 eq(layer.GetClassification().GetDefaultFill(), Transparent)
255 eq(layer.GetClassification().GetDefaultLineColor().hex(), "#000000")
256 eq(layer.Visible(), True)
257
258 self.check_format()
259
260 self.session.Destroy()
261 self.session = None
262
263 def test_leak(self):
264 """Test load_session for resource leaks
265
266 The load_session function had a resource leak in that it created
267 cyclic references. The objects would have been eventually
268 collected by the garbage collector but too late. One symptom is
269 that when layers are removed so that the last normal reference
270 owned indirectly by the session to a shape store goes away, the
271 shape store is not actually removed from the session even though
272 the session only keeps weak references because there are still
273 references owned by the cyclic garbage.
274 """
275 session = load_session(self.filename())
276 self.session = session
277
278 # sanity check
279 self.assertEquals(len(session.ShapeStores()), 1)
280
281 # remove the map. The shapestore should go away too
282 session.RemoveMap(session.Maps()[0])
283 self.assertEquals(len(session.ShapeStores()), 0)
284
285
286 class TestNonAsciiColumnName(LoadSessionTest):
287
288 file_contents = '''\
289 <?xml version="1.0" encoding="UTF-8"?>
290 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
291 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
292 title="Non ASCII column name test">
293 <fileshapesource filetype="shapefile" id="D1"
294 filename="TestNonAsciiColumnName.shp"/>
295 <map title="map">
296 <projection name="Some Projection">
297 <parameter value="datum=WGS84"/>
298 <parameter value="ellps=WGS84"/>
299 <parameter value="proj=utm"/>
300 <parameter value="units=m"/>
301 <parameter value="zone=27"/>
302 </projection>
303 <layer shapestore="D1" visible="true" title="layer">
304 <classification field="Fl\xc3\xa4che" field_type="double">
305 <clnull label="">
306 <cldata stroke="#000000" stroke_width="1" fill="None"/>
307 </clnull>
308 </classification>
309 </layer>
310 </map>
311 </session>
312 '''
313
314 def test(self):
315 """Load a session with a single map with a single layer"""
316
317 # Create a shapefile and a dbffile with a non-ascii column name
318 dbffile = self.temp_file_name("TestNonAsciiColumnName.dbf")
319 shpfile = self.temp_file_name("TestNonAsciiColumnName.shp")
320 dbf = dbflib.create(dbffile)
321 dbf.add_field('Fl\xe4che', dbflib.FTDouble, 10, 5)
322 dbf.write_record(0, (0.0,))
323 dbf.close()
324 shp = shapelib.create(shpfile, shapelib.SHPT_POLYGON)
325 shp.write_object(-1, shapelib.SHPObject(shapelib.SHPT_POLYGON, 1,
326 [[(0,0), (10, 10), (10, 0),
327 (0, 0)]]))
328 shp.close()
329
330 try:
331 session = load_session(self.filename())
332 except ValueError, v:
333 # Usually if the field name is not decoded properly the
334 # loading fails because the field type mentioned in the file
335 # is not None as returned from the layer for a non-existing
336 # column name so we check for that and report it as failure.
337 # Other exceptions are errors in the test case.
338 if str(v) == "xml field type differs from database!":
339 self.fail("Cannot load file with non-ascii column names")
340 else:
341 raise
342 self.session = session
343
344 # In case Thuban could load the file anyway (i.e. no ValueError
345 # exception in load_session()), check explicitly whether the
346 # field name was decoded properly. The test will probably lead
347 # to a UnicodeError instead of a test failure so we check that
348 # too
349 layer = session.Maps()[0].Layers()[0]
350 try:
351 self.assertEquals(layer.GetClassificationColumn(), 'Fl\xe4che')
352 except UnicodeError:
353 # FIXME: Obviously this will have to change if Thuban ever
354 # supports unicode properly.
355 self.fail("Column name was not converted to a bytestring")
356
357 # roundtrip check
358 self.check_format()
359
360
361 class TestLayerVisibility(LoadSessionTest):
362
363 file_contents = '''\
364 <?xml version="1.0" encoding="UTF-8"?>
365 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
366 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
367 title="single map&amp;layer">
368 <fileshapesource filetype="shapefile" id="D1"
369 filename="../../Data/iceland/political.shp"/>
370 <map title="Test Map">
371 <projection name="Unknown">
372 <parameter value="zone=26"/>
373 <parameter value="proj=utm"/>
374 <parameter value="ellps=clrk66"/>
375 </projection>
376 <layer shapestore="D1" visible="false" title="My Layer">
377 <classification>
378 <clnull label="">
379 <cldata stroke="#000000" stroke_width="1" fill="None"/>
380 </clnull>
381 </classification>
382 </layer>
383 </map>
384 </session>
385 '''
386
387 def test(self):
388 """Test that the visible flag is correctly loaded for a layer."""
389 eq = self.assertEquals
390 session = load_session(self.filename())
391 self.session = session
392 maps = session.Maps()
393 eq(len(maps), 1)
394 map = maps[0]
395 layers = map.Layers()
396 eq(len(layers), 1)
397 layer = layers[0]
398
399 eq(layer.Visible(), False)
400
401 self.check_format()
402
403
404 class TestSymbolSize(ClassificationTest):
405
406 file_contents = '''\
407 <?xml version="1.0" encoding="UTF-8"?>
408 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
409 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd" title="Thuban sample session">
410 <fileshapesource filetype="shapefile" id="D813968480" filename="../../Data/iceland/cultural_landmark-point.shp"/>
411 <map title="Iceland map">
412 <layer title="cultural_landmark-point" shapestore="D813968480" visible="true">
413 <classification field="CLPTLABEL" field_type="string">
414 <clnull label="">
415 <cldata stroke="#000000" stroke_width="1" size="3" fill="#000000"/>
416 </clnull>
417 <clpoint label="" value="RUINS">
418 <cldata stroke="#000000" stroke_width="1" size="6" fill="#ffffff"/>
419 </clpoint>
420 <clpoint label="" value="FARM">
421 <cldata stroke="#000000" stroke_width="1" size="9" fill="#ffff00"/>
422 </clpoint>
423 </classification>
424 </layer>
425 </map>
426 </session>
427 '''
428
429 def test(self):
430 """Test that the size attribute for point symbols is correctly
431 loaded for a layer."""
432 eq = self.assertEquals
433 session = load_session(self.filename())
434 self.session = session
435
436 map = session.Maps()[0] # only one map in the sample
437
438 expected = [("cultural_landmark-point", 2,
439 [("default", (), "",
440 ("#000000", 1, "#000000", 3)),
441 ("single", "RUINS", "",
442 ("#000000", 1, "#ffffff", 6)),
443 ("single", "FARM", "",
444 ("#000000", 1, "#ffff00", 9))])]
445
446 self.TestLayers(map.Layers(), expected)
447
448 self.check_format()
449
450
451 class TestClassification(ClassificationTest):
452
453 file_contents = '''\
454 <?xml version="1.0" encoding="UTF-8"?>
455 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
456 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
457 title="single map&amp;layer">
458 <fileshapesource filetype="shapefile" id="D138389860"
459 filename="../../Data/iceland/political.shp"/>
460 <fileshapesource filetype="shapefile" id="D138504492"
461 filename="../../Data/iceland/political.shp"/>
462 <fileshapesource filetype="shapefile" id="D123456789"
463 filename="../../Data/iceland/cultural_landmark-point.shp"/>
464 <map title="Test Map">
465 <projection name="">
466 <parameter value="zone=26"/>
467 <parameter value="proj=utm"/>
468 <parameter value="ellps=clrk66"/>
469 </projection>
470 <layer shapestore="D138389860" visible="true" title="My Layer">
471 <classification field="POPYREG" field_type="string">
472 <clnull label="">
473 <cldata stroke="#000000" stroke_width="1" fill="None"/>
474 </clnull>
475 <clpoint label="" value="1">
476 <cldata stroke="#000000" stroke_width="2" fill="None"/>
477 </clpoint>
478 <clpoint label="" value="1">
479 <cldata stroke="#000000" stroke_width="10" fill="None"/>
480 </clpoint>
481 <clpoint label="\xc3\x9cml\xc3\xa4uts"
482 value="\xc3\xa4\xc3\xb6\xc3\xbc">
483 <cldata stroke="#000000" stroke_width="1" fill="None"/>
484 </clpoint>
485 </classification>
486 </layer>
487 <layer shapestore="D138504492" visible="true" title="My Layer 2">
488 <classification field="AREA" field_type="double">
489 <clnull label="">
490 <cldata stroke="#000000" stroke_width="2" fill="None"/>
491 </clnull>
492 <clrange label="" range="[0;1[">
493 <cldata stroke="#111111" stroke_width="1" fill="None"/>
494 </clrange>
495 <clpoint label="" value="0.5">
496 <cldata stroke="#000000" stroke_width="1" fill="#111111"/>
497 </clpoint>
498 <clrange label="" range="[-1;0[">
499 <cldata stroke="#000000" stroke_width="1" fill="None"/>
500 </clrange>
501 <clpoint label="" value="-0.5">
502 <cldata stroke="#000000" stroke_width="1" fill="None"/>
503 </clpoint>
504 </classification>
505 </layer>
506 <layer shapestore="D123456789" visible="true" title="My Layer 3">
507 <classification field="CLPTLABEL" field_type="string">
508 <clnull label="">
509 <cldata stroke="#000000" size="5" stroke_width="2" fill="None"/>
510 </clnull>
511 <clpoint label="" value="FARM">
512 <cldata stroke="#111111" size="5" stroke_width="1" fill="None"/>
513 </clpoint>
514 <clpattern label="" pattern="BUI">
515 <cldata stroke="#000000" size="5" stroke_width="1" fill="None"/>
516 </clpattern>
517 </classification>
518 </layer>
519 </map>
520 </session>
521 '''
522
523 def test(self):
524 """Load a Thuban session with a map and classified layers."""
525 session = load_session(self.filename())
526 self.session = session
527
528 map = self.session.Maps()[0] # only one map in the sample
529
530 expected = [("My Layer", 3,
531 [("default", (), "",
532 ("#000000", 1, "None")),
533 ("single", "1", "",
534 ("#000000", 2, "None")),
535 ("single", "1", "",
536 ("#000000", 10, "None")),
537 ("single", internal_from_unicode(u"\xe4\xf6\xfc"),
538 internal_from_unicode(u"\xdcml\xe4uts"),
539 ("#000000", 1, "None"))]),
540 ("My Layer 2", 4,
541 [("default", (), "",
542 ("#000000", 2, "None")),
543 ("range", (0, 1), "",
544 ("#111111", 1, "None")),
545 ("single", .5, "",
546 ("#000000", 1, "#111111")),
547 ("range", (-1, 0), "",
548 ("#000000", 1, "None")),
549 ("single", -.5, "",
550 ("#000000", 1, "None"))]),
551 ("My Layer 3", 2,
552 [("default", (), "",
553 ("#000000", 2, "None")),
554 ("single", "FARM", "",
555 ("#111111", 1, "None")),
556 ("pattern", "BUI", "",
557 ("#000000", 1, "None"))]),
558 ]
559
560 self.TestLayers(map.Layers(), expected)
561
562 self.check_format()
563
564
565 class TestLabels(ClassificationTest):
566
567 file_contents = '''\
568 <?xml version="1.0" encoding="UTF-8"?>
569 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
570 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
571 title="single map&amp;layer">
572 <fileshapesource filetype="shapefile" id="D1"
573 filename="../../Data/iceland/political.shp"/>
574 <map title="Test Map">
575 <projection name="Unknown">
576 <parameter value="zone=26"/>
577 <parameter value="proj=utm"/>
578 <parameter value="ellps=clrk66"/>
579 </projection>
580 <layer shapestore="D1" visible="true" title="My Layer">
581 <classification field="POPYREG" field_type="string">
582 <clnull label="hallo">
583 <cldata stroke="#000000" stroke_width="1" fill="None"/>
584 </clnull>
585 <clpoint label="welt" value="1">
586 <cldata stroke="#000000" stroke_width="2" fill="None"/>
587 </clpoint>
588 </classification>
589 </layer>
590 </map>
591 </session>
592 '''
593
594 def test(self):
595 """Load a session and test for reading the group labels."""
596 eq = self.assertEquals
597 session = load_session(self.filename())
598 self.session = session
599
600 map = self.session.Maps()[0] # only one map in the sample
601
602 expected = [("My Layer", 1,
603 [("default", (), "hallo",
604 ("#000000", 1, "None")),
605 ("single", "1", "welt",
606 ("#000000", 2, "None"))])]
607
608 self.TestLayers(map.Layers(), expected)
609 self.check_format()
610
611
612 class TestLayerProjection(LoadSessionTest):
613
614 file_contents = '''\
615 <?xml version="1.0" encoding="UTF-8"?>
616 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
617 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
618 title="single map&amp;layer">
619 <fileshapesource filetype="shapefile" id="D2"
620 filename="../../Data/iceland/roads-line.shp"/>
621 <fileshapesource filetype="shapefile" id="D4"
622 filename="../../Data/iceland/political.shp"/>
623 <map title="Test Map">
624 <projection name="Unknown">
625 <parameter value="zone=26"/>
626 <parameter value="proj=utm"/>
627 <parameter value="ellps=clrk66"/>
628 </projection>
629 <layer shapestore="D4" visible="true" title="My Layer">
630 <projection name="hello">
631 <parameter value="zone=13"/>
632 <parameter value="proj=tmerc"/>
633 <parameter value="ellps=clrk66"/>
634 </projection>
635 <classification field="POPYREG" field_type="string">
636 <clnull label="hallo">
637 <cldata stroke="#000000" stroke_width="1" fill="None"/>
638 </clnull>
639 <clpoint label="welt" value="1">
640 <cldata stroke="#000000" stroke_width="2" fill="None"/>
641 </clpoint>
642 </classification>
643 </layer>
644 <layer shapestore="D2" visible="true" title="My Layer">
645 <projection name="Unknown">
646 <parameter value="proj=lcc"/>
647 <parameter value="lat_1=10"/>
648 <parameter value="lat_2=20"/>
649 <parameter value="ellps=clrk66"/>
650 </projection>
651 <classification>
652 <clnull label="">
653 <cldata stroke="#000000" stroke_width="1" fill="None"/>
654 </clnull>
655 </classification>
656 </layer>
657 </map>
658 </session>
659 '''
660
661 def test(self):
662 """Test loading layers with projections"""
663 eq = self.assertEquals
664 neq = self.assertNotEqual
665
666 session = load_session(self.filename())
667 self.session = session
668
669 map = self.session.Maps()[0] # only one map in the sample
670
671 layers = map.Layers() # two layers in the sample
672
673 # test layer with a named projection
674 proj = layers[0].GetProjection()
675 neq(proj, None)
676 eq(proj.GetName(), "hello")
677 eq(proj.GetParameter("proj"), "tmerc")
678 eq(proj.GetParameter("zone"), "13")
679 eq(proj.GetParameter("ellps"), "clrk66")
680
681 # test layer with an unnamed projection
682 proj = layers[1].GetProjection()
683 neq(proj, None)
684 eq(proj.GetName(), "Unknown")
685 eq(proj.GetParameter("proj"), "lcc")
686 eq(proj.GetParameter("lat_1"), "10")
687 eq(proj.GetParameter("lat_2"), "20")
688 eq(proj.GetParameter("ellps"), "clrk66")
689
690 self.check_format()
691
692
693 class TestRasterLayer(LoadSessionTest):
694
695 file_contents = '''\
696 <?xml version="1.0" encoding="UTF-8"?>
697 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
698 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
699 title="single map&amp;layer">
700 <map title="Test Map">
701 <rasterlayer visible="false" filename="../../Data/iceland/island.tif"
702 title="My RasterLayer" opacity="0.4" masktype="alpha"/>
703 </map>
704 </session>
705 '''
706
707 def test(self):
708 eq = self.assertEquals
709 neq = self.assertNotEqual
710
711 session = load_session(self.filename())
712 self.session = session
713
714 map = self.session.Maps()[0] # only one map in the sample
715
716 layer = map.Layers()[0] # one layer in the sample
717
718 eq(layer.Title(), "My RasterLayer")
719 eq(layer.Opacity(), 0.4)
720 eq(layer.MaskType(), layer.MASK_ALPHA)
721
722 self.failIf(layer.Visible())
723 self.failUnless(filenames_equal(layer.GetImageFilename(),
724 os.path.join(self.temp_dir(),
725 os.pardir, os.pardir,
726 "Data", "iceland",
727 "island.tif")))
728 self.check_format()
729
730
731 class TestJoinedTable(LoadSessionTest):
732
733 file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
734 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
735 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd" title="A Joined Table session">
736 <fileshapesource filetype="shapefile" id="D137227612"
737 filename="../../Data/iceland/roads-line.shp"/>
738 <filetable filetype="DBF" filename="load_joinedtable.dbf" id="D136171140"
739 title="Some Title"/>
740 <jointable id="D136169900" title="Joined"
741 right="D136171140" left="D137227612"
742 leftcolumn="RDLNTYPE" rightcolumn="RDTYPE"
743 jointype="LEFT OUTER"/>
744 <derivedshapesource table="D136169900" shapesource="D137227612"
745 id="D136170932"/>
746 <map title="Test Map">
747 <layer shapestore="D136170932" visible="true" title="My Layer">
748 <classification>
749 <clnull label="">
750 <cldata stroke="#000000" stroke_width="1" fill="None"/>
751 </clnull>
752 </classification>
753 </layer>
754 </map>
755 </session>
756 '''
757
758 def setUp(self):
759 """Extend inherited method to create the dbffile for the join"""
760 LoadSessionTest.setUp(self)
761 dbffile = self.temp_file_name("load_joinedtable.dbf")
762 dbf = dbflib.create(dbffile)
763 dbf.add_field("RDTYPE", dbflib.FTInteger, 10, 0)
764 dbf.add_field("TEXT", dbflib.FTString, 10, 0)
765 dbf.write_record(0, {'RDTYPE': 8, "TEXT": "foo"})
766 dbf.write_record(1, {'RDTYPE': 2, "TEXT": "bar"})
767 dbf.write_record(2, {'RDTYPE': 3, "TEXT": "baz"})
768 dbf.close()
769
770 def test(self):
771 """Test loading a session containing a joined table"""
772 session = load_session(self.filename())
773 self.session = session
774
775 tables = session.Tables()
776 self.assertEquals(len(tables), 3)
777 # FIXME: The tests shouldn't assume a certain order of the tables
778 self.assertEquals(tables[0].Title(), "Some Title")
779 self.assertEquals(tables[1].Title(), "Joined")
780 self.assertEquals(tables[1].JoinType(), "LEFT OUTER")
781 self.check_format()
782
783
784 class TestLabelLayer(LoadSessionTest):
785
786 # Note that the labels deliberately contain non-ascii characters to
787 # test whether they're supported correctly.
788
789 file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
790 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
791 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd" title="Thuban sample session">
792 <fileshapesource filetype="shapefile" id="D145265052"
793 filename="../../Data/iceland/political.shp"/>
794 <fileshapesource filetype="shapefile" id="D145412868"
795 filename="../../Data/iceland/cultural_landmark-point.shp"/>
796 <map title="Iceland map">
797 <projection name="Unknown">
798 <parameter value="zone=26"/>
799 <parameter value="proj=utm"/>
800 <parameter value="ellps=clrk66"/>
801 </projection>
802 <layer shapestore="D145265052" visible="true" title="political">
803 <projection name="Geographic">
804 <parameter value="proj=latlong"/>
805 <parameter value="to_meter=0.017453"/>
806 <parameter value="ellps=clrk66"/>
807 </projection>
808 <classification>
809 <clnull label="">
810 <cldata stroke="#000000" stroke_width="1" fill="#c0c0c0"/>
811 </clnull>
812 </classification>
813 </layer>
814 <layer shapestore="D145412868" visible="true" title="landmarks">
815 <projection name="Geographic">
816 <parameter value="proj=latlong"/>
817 <parameter value="to_meter=0.017453"/>
818 <parameter value="ellps=clrk66"/>
819 </projection>
820 <classification>
821 <clnull label="">
822 <cldata size="5" stroke="#000000" stroke_width="1" fill="#ffff00"/>
823 </clnull>
824 </classification>
825 </layer>
826 <labellayer>
827 <label x="-21.5" y="64.25" text="RUINS"
828 halign="left" valign="center"/>
829 <label x="-15.125" y="64.75" text="H\xc3\xbctte"
830 halign="right" valign="top"/>
831 </labellayer>
832 </map>
833 </session>
834 '''
835
836 def test(self):
837 """Test loading a session with a label layer"""
838 session = load_session(self.filename())
839 self.session = session
840
841 label_layer = self.session.Maps()[0].LabelLayer()
842 expected_labels = [(-21.5, 64.25, "RUINS", ALIGN_LEFT, ALIGN_CENTER),
843 (-15.125, 64.75, internal_from_unicode(u"H\xfctte"),
844 ALIGN_RIGHT, ALIGN_TOP),
845 ]
846 for label, values in zip(label_layer.Labels(), expected_labels):
847 self.assertEquals((label.x, label.y, label.text, label.halign,
848 label.valign),
849 values)
850 self.check_format()
851
852
853 class TestPostGISLayer(LoadSessionTest):
854
855 file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
856 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
857 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
858 title="unnamed session">
859 <dbconnection port="%(port)s" host="%(host)s" user="%(user)s"
860 dbtype="postgis" id="D142684948" dbname="%(dbname)s"/>
861 <dbshapesource id="D143149420" dbconn="D142684948"
862 tablename="landmarks_point_id" id_column="point_id"
863 geometry_column="the_geom" />
864 <map title="unnamed map">
865 <layer shapestore="D143149420" visible="true" stroke="#000000"
866 title="landmarks" stroke_width="1" fill="None"/>
867 </map>
868 </session>
869 '''
870
871 def setUp(self):
872 """Extend the inherited method to start the postgis server
873
874 Furthermore, patch the file contents with the real postgis db
875 information
876 """
877 postgissupport.skip_if_no_postgis()
878 self.server = postgissupport.get_test_server()
879 self.postgisdb = self.server.get_default_static_data_db()
880
881 self.file_contents = self.__class__.file_contents % {
882 "dbname": self.postgisdb.dbname,
883 "user": self.server.user_name,
884 "port": self.server.port,
885 "host": self.server.host}
886 LoadSessionTest.setUp(self)
887
888 def test(self):
889 """Test loading a session containing a postgis shapestore"""
890 session = load_session(self.filename())
891 self.session = session
892 connections = session.DBConnections()
893 self.assertEquals(len(connections), 1)
894 conn = connections[0]
895 for attr, value in [("host", self.server.host),
896 ("port", str(self.server.port)),
897 ("user", self.server.user_name),
898 ("dbname", self.postgisdb.dbname)]:
899 self.assertEquals(getattr(conn, attr), value)
900 layer = session.Maps()[0].Layers()[0]
901 self.failUnless(layer.ShapeStore().DBConnection() is conn)
902
903
904 class TestPostGISLayerPassword(LoadSessionTest):
905
906 file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
907 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
908 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
909 title="unnamed session">
910 <dbconnection port="%(port)s" host="%(host)s" user="%(user)s"
911 dbtype="postgis" id="D142684948" dbname="%(dbname)s"/>
912 <dbshapesource tablename="landmarks" id="D143149420" dbconn="D142684948"/>
913 <map title="unnamed map">
914 <layer shapestore="D143149420" visible="true" stroke="#000000"
915 title="landmarks" stroke_width="1" fill="None"/>
916 </map>
917 </session>
918 '''
919
920 def setUp(self):
921 """Extend the inherited method to start the postgis server
922
923 Furthermore, patch the file contents with the real postgis db
924 information
925 """
926 postgissupport.skip_if_no_postgis()
927 self.server = postgissupport.get_test_server()
928 self.postgisdb = self.server.get_default_static_data_db()
929
930 self.file_contents = self.__class__.file_contents % {
931 "dbname": self.postgisdb.dbname,
932 "user": self.server.user_name,
933 "port": self.server.port,
934 "host": self.server.host}
935 LoadSessionTest.setUp(self)
936
937 self.db_connection_callback_called = False
938 self.server.require_authentication(True)
939
940 def tearDown(self):
941 """Extend the inherited method to switch off postgresql authentication
942 """
943 self.server.require_authentication(False)
944 LoadSessionTest.tearDown(self)
945
946 def db_connection_callback(self, params, message):
947 """Implementation of Thuban.Model.hooks.query_db_connection_parameters
948 """
949 self.assertEquals(params,
950 {"dbname": self.postgisdb.dbname,
951 "user": self.server.user_name,
952 "port": str(self.server.port),
953 "host": self.server.host})
954 self.db_connection_callback_called = True
955 params = params.copy()
956 params["password"] = self.server.user_password
957 return params
958
959 def test_with_callback(self):
960 """Test loading a session with postgis, authentication and a callback
961 """
962 session = load_session(self.filename(),
963 db_connection_callback = self.db_connection_callback)
964 self.session = session
965 connections = session.DBConnections()
966 self.assertEquals(len(connections), 1)
967 conn = connections[0]
968 for attr, value in [("host", self.server.host),
969 ("port", str(self.server.port)),
970 ("user", self.server.user_name),
971 ("dbname", self.postgisdb.dbname)]:
972 self.assertEquals(getattr(conn, attr), value)
973 layer = session.Maps()[0].Layers()[0]
974 self.failUnless(layer.ShapeStore().DBConnection() is conn)
975 self.failUnless(self.db_connection_callback_called)
976
977 def test_without_callback(self):
978 """Test loading a session with postgis, authentication and no callback
979 """
980 # A password is required and there's no callback, so we should
981 # get a ConnectionError
982 self.assertRaises(ConnectionError, load_session, self.filename())
983
984 def test_cancel(self):
985 """Test loading a session with postgis and cancelling authentication
986 """
987 def cancel(*args):
988 self.db_connection_callback_called = True
989 return None
990
991 # If the user cancels, i.e. if the callbakc returns None, a
992 # LoadCancelled exception is raised.
993 self.assertRaises(LoadCancelled,
994 load_session, self.filename(), cancel)
995 self.failUnless(self.db_connection_callback_called)
996
997
998 class TestLoadError(LoadSessionTest):
999
1000 file_contents = '''\
1001 <?xml version="1.0" encoding="UTF-8"?>
1002 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
1003 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
1004 title="single map&amp;layer">
1005 <fileshapesource id="D1" filename="../../Data/iceland/political.shp"/>
1006 <map title="Test Map">
1007 <projection name="Unknown">
1008 <parameter value="zone=26"/>
1009 <parameter value="proj=utm"/>
1010 <parameter value="ellps=clrk66"/>
1011 </projection>
1012 <layer shapestore="D1" visible="true"
1013 stroke="#000000" title="My Layer" stroke_width="1"
1014 fill="None"/>
1015 </map>
1016 </session>
1017 '''
1018
1019 def test(self):
1020 """Test loading a session missing a required attribute"""
1021 # Don't use assertRaises to make sure that if a session is
1022 # actually returned it gets destroyed properly.
1023 try:
1024 self.session = load_session(self.filename())
1025 except LoadError, value:
1026 # Check the actual messge in value to make sure the
1027 # LoadError really was about the missing attribute
1028 self.assertEquals(str(value),
1029 "Element "
1030 "(u'http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd',"
1031 " u'fileshapesource') requires an attribute 'filetype'")
1032 else:
1033 self.fail("Missing filetype attribute doesn't raise LoadError")
1034
1035 class Shapefile_CallBack:
1036
1037 def __init__(self, params):
1038 """Initialize the callback return values.
1039
1040 params must be a dictionary of the potential CB modes (keys),
1041 with lists of tuples of return values as values.
1042 Depending on the test the callback can be called multiple,
1043 each time a return value is poped from the list
1044 """
1045
1046 self.params = params
1047
1048
1049 def s_cb(self, filename, mode = None, second_try= 0):
1050 if self.params.has_key(mode):
1051 return self.params[mode].pop(0)
1052 else:
1053 raise LoadError
1054
1055 class TestAltPath(LoadSessionTest):
1056
1057 """Test the various cases in the alternative path feature.
1058
1059 The test checks the reasonable cases:
1060 - First recognition of a path error, fixed with user interaction.
1061 - First recognition of a path error, load cancelled.
1062 - Path error fixed from list, confirmed by user.
1063 - Path error fixed from list, changed by user.
1064 - Path error fixed from list, cancelled by user.
1065 - Path error wrongly fixed from list, manual fix forced.
1066 """
1067
1068 file_contents = '''\
1069 <?xml version="1.0" encoding="UTF-8"?>
1070 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
1071 <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd" title="AltPath Test session">
1072 <fileshapesource filetype="shapefile" id="D1108450956" filename="../../Data/iceland/political.shp"/>
1073 <fileshapesource filetype="shapefile" id="D1108900076" filename="../Data/iceland/roads-line.shp"/>
1074 <fileshapesource filetype="shapefile" id="D1108947244" filename="../../Data/iceland/cultural_landmark-point.shp"/>
1075 <map title="not the iceland map">
1076 <layer title="political" stroke_width="1" shapestore="D1108450956" visible="true" stroke="#000000" fill="#c0c0c0"/>
1077 <layer title="roads-line" stroke_width="1" shapestore="D1108900076" visible="true" stroke="#000000" fill="None"/>
1078 <layer title="something else" stroke_width="1" shapestore="D1108947244" visible="true" stroke="#000000" fill="None"/>
1079 </map>
1080 </session>
1081 '''
1082
1083 def checkSession(self, session):
1084 """Check if session has been loaded successfully."""
1085
1086 eq = self.assertEquals
1087
1088 map = session.Maps()[0]
1089 layers = map.Layers()
1090
1091 eq("AltPath Test session", session.Title())
1092 eq("not the iceland map", map.Title())
1093 eq(3,len(layers))
1094 eq("political",layers[0].Title())
1095 eq("roads-line",layers[1].Title())
1096 eq("something else",layers[2].Title())
1097
1098 def test_01_single_path_error_fix(self):
1099 """Test single file path error fix."""
1100 # The usual initial case
1101 s_cb = Shapefile_CallBack({
1102 "search": [("../Data/iceland/roads-line.shp",0)],
1103 "check": [(None, None)]})
1104 self.session = load_session(self.filename(),
1105 shapefile_callback =s_cb.s_cb)
1106 self.checkSession(self.session)
1107
1108 def test_02_path_error_fix_from_list(self):
1109 """Test single file path error fix."""
1110 # This represents the usual case for "from_list"
1111 s_cb = Shapefile_CallBack({
1112 "search": [("../Data/iceland/roads-line.shp",1)],
1113 "check": [(os.path.abspath("../Data/iceland/roads-line.shp"),1)]
1114 })
1115 self.session = load_session(self.filename(),
1116 shapefile_callback =s_cb.s_cb)
1117 self.checkSession(self.session)
1118
1119 def test_03_single_path_error_cancelled(self):
1120 """Test alternative path cancelled."""
1121 s_cb = Shapefile_CallBack({
1122 "search": [(None,0)],
1123 "check": [(None, None)]})
1124 self.assertRaises(LoadCancelled,
1125 load_session, self.filename(), None, s_cb.s_cb)
1126
1127 def test_04_path_error_fix_from_list_cancelled(self):
1128 """Test alternative path from list cancelled."""
1129 s_cb = Shapefile_CallBack({
1130 "search": [("../Data/iceland/roads-line.shp",1)],
1131 "check": [(None,1)]
1132 })
1133 self.assertRaises(LoadCancelled,
1134 load_session, self.filename(), None, s_cb.s_cb)
1135
1136 def test_05_path_error_fix_from_list_changed(self):
1137 """Test alternative path from list changed."""
1138 s_cb = Shapefile_CallBack({
1139 "search": [("../Data/iceland/roads-line.shp",1)],
1140 "check": [("../Data/iceland/roads-line.shp",0)]
1141 })
1142 self.session = load_session(self.filename(),
1143 shapefile_callback =s_cb.s_cb)
1144 self.checkSession(self.session)
1145
1146 def test_06_path_error_fix_from_list_fails(self):
1147 """Test alternative path recovery from list."""
1148 s_cb = Shapefile_CallBack({
1149 "search": [("../wrong/iceland/roads-line.shp",1),
1150 ("../Data/iceland/roads-line.shp",0)],
1151 "check": [(None,None)]
1152 })
1153 self.session = load_session(self.filename(),
1154 shapefile_callback =s_cb.s_cb)
1155 self.assertRaises(IndexError,
1156 s_cb.s_cb, None, "search")
1157
1158
1159
1160 if __name__ == "__main__":
1161 support.run_tests()

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26