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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2655 - (show annotations)
Wed Jul 27 21:45:38 2005 UTC (19 years, 7 months ago) by jan
Original Path: trunk/thuban/test/test_save.py
File MIME type: text/x-python
File size: 25730 byte(s)
(SaveSessionTest.testSingleLayer, SaveSessionTest.testLayerProjection,
SaveSessionTest.testClassifiedLayer, SaveSessionTest.test_joined_table,
SaveSessionTest.test_save_postgis): Removed attributes from layer
element to classification clnull element.
(test_save_postgis.NonConnectionStore._fetch_table_information): added
pretending to have a shape_type.

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 saving a thuban session as XML
10 """
11
12 __version__ = "$Revision$"
13 # $Source$
14 # $Id$
15
16 import os
17 import unittest
18 from StringIO import StringIO
19
20 import xmlsupport
21 import postgissupport
22
23 import support
24 support.initthuban()
25
26 import dbflib
27
28 from Thuban import internal_from_unicode
29 from Thuban.Lib.fileutil import relative_filename
30 from Thuban.Model.save import XMLWriter, save_session, sort_data_stores
31 from Thuban.Model.session import Session
32 from Thuban.Model.map import Map
33 from Thuban.Model.layer import Layer, RasterLayer
34 from Thuban.Model.proj import Projection
35 from Thuban.Model.table import DBFTable
36 from Thuban.Model.transientdb import TransientJoinedTable
37 from Thuban.Model.data import DerivedShapeStore, SHAPETYPE_ARC
38
39 from Thuban.Model.classification import ClassGroupSingleton, ClassGroupRange, \
40 ClassGroupProperties
41
42 from Thuban.Model.range import Range
43
44 from Thuban.Model.postgisdb import PostGISConnection, PostGISShapeStore
45
46
47 class XMLWriterTest(unittest.TestCase):
48
49 def testEncode(self):
50 """Test XMLWriter.encode"""
51 writer = XMLWriter()
52 eq = self.assertEquals
53
54 eq(writer.encode("hello world"), "hello world")
55 eq(writer.encode(unicode("hello world")), unicode("hello world"))
56
57 eq(writer.encode(internal_from_unicode(u"\x80\x90\xc2\x100")),
58 "\xc2\x80\xc2\x90\xc3\x82\x100")
59 eq(writer.encode(u"\x80\x90\xc2\x100"),
60 "\xc2\x80\xc2\x90\xc3\x82\x100")
61 eq(writer.encode(u"\xFF5E"), "\xc3\xbf5E")
62
63 eq(writer.encode('&"\'<>'), "&amp;&quot;&apos;&lt;&gt;")
64 eq(writer.encode(unicode('&"\'<>')), "&amp;&quot;&apos;&lt;&gt;")
65
66 class SaveSessionTest(unittest.TestCase, support.FileTestMixin,
67 xmlsupport.ValidationTest):
68
69 dtd = "http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
70 thubanids = [((dtd, n), (None, "id")) for n in
71 ["fileshapesource", "filetable", "jointable",
72 "derivedshapesource", "dbshapesource", "dbconnection"]]
73 thubanidrefs = [((dtd, n), (None, m)) for n, m in
74 [("layer", "shapestore"),
75 ("jointable", "left"),
76 ("jointable", "right"),
77 ("derivedshapesource", "table"),
78 ("derivedshapesource", "shapesource"),
79 ("dbshapesource", "dbconn")]]
80 del n, m, dtd
81
82 def tearDown(self):
83 """Call self.session.Destroy
84
85 Test cases that create session should bind it to self.session so
86 that it gets destroyed properly
87 """
88 if hasattr(self, "session"):
89 self.session.Destroy()
90 self.session = None
91
92 def compare_xml(self, xml1, xml2):
93 list1 = xmlsupport.sax_eventlist(xml1, ids = self.thubanids,
94 idrefs = self.thubanidrefs)
95 list2 = xmlsupport.sax_eventlist(xml2, ids = self.thubanids,
96 idrefs = self.thubanidrefs)
97 if list1 != list2:
98 for a, b in zip(list1, list2):
99 if a != b:
100 self.fail("%r != %r" % (a, b))
101
102
103 def testEmptySession(self):
104 """Save an empty session"""
105 session = Session("empty session")
106 filename = self.temp_file_name("save_emptysession.thuban")
107 save_session(session, filename)
108 session.Destroy()
109
110 file = open(filename)
111 written_contents = file.read()
112 file.close()
113 self.compare_xml(written_contents,
114 '<?xml version="1.0" encoding="UTF-8"?>\n'
115 '<!DOCTYPE session SYSTEM "thuban-1.1.dtd">\n'
116 '<session title="empty session" '
117 'xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">'
118 '\n</session>\n')
119
120 self.validate_data(written_contents)
121
122 def testSingleLayer(self):
123 """Save a session with a single map with a single layer"""
124 # deliberately put an apersand in the title :)
125 session = Session("single map&layer")
126 proj = Projection(["proj=utm", "zone=27", "ellps=WGS84",
127 "datum=WGS84", "units=m"],
128 name = "WGS 84 / UTM zone 27N",
129 epsg = "32627")
130 map = Map("Test Map", projection = proj)
131 session.AddMap(map)
132 # use shapefile from the example data
133 shpfile = os.path.join(os.path.dirname(__file__),
134 os.pardir, "Data", "iceland", "political.shp")
135 layer = Layer("My Layer", session.OpenShapefile(shpfile))
136 map.AddLayer(layer)
137
138 filename = self.temp_file_name("save_singlemap.thuban")
139 save_session(session, filename)
140
141 file = open(filename)
142 written_contents = file.read()
143 file.close()
144 expected_template = '''<?xml version="1.0" encoding="UTF-8"?>
145 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
146 <session title="single map&amp;layer"
147 xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">
148 <fileshapesource id="D1"
149 filename="../../Data/iceland/political.shp"
150 filetype="shapefile"/>
151 <map title="Test Map">
152 <projection epsg="32627" name="WGS 84 / UTM zone 27N">
153 <parameter value="proj=utm"/>
154 <parameter value="zone=27"/>
155 <parameter value="ellps=WGS84"/>
156 <parameter value="datum=WGS84"/>
157 <parameter value="units=m"/>
158 </projection>
159 <layer title="My Layer" shapestore="D1" visible="%s">
160 <classification>
161 <clnull label="">
162 <cldata fill="None" stroke="#000000"
163 stroke_width="1"/>
164 </clnull>
165 </classification>
166 </layer>
167 </map>
168 </session>'''
169
170 expected_contents = expected_template % "true"
171
172 self.compare_xml(written_contents, expected_contents)
173
174 self.validate_data(written_contents)
175
176 # Repeat with an invisible layer
177 layer.SetVisible(False)
178 save_session(session, filename)
179
180 file = open(filename)
181 written_contents = file.read()
182 file.close()
183 expected_contents = expected_template % "false"
184 self.compare_xml(written_contents, expected_contents)
185 self.validate_data(written_contents)
186
187 session.Destroy()
188
189 def testLayerProjection(self):
190 """Test saving layers with projections"""
191 # deliberately put an apersand in the title :)
192 session = self.session = Session("single map&layer")
193 proj = Projection(["zone=26", "proj=utm", "ellps=clrk66"])
194 map = Map("Test Map", projection = proj)
195 session.AddMap(map)
196 # use shapefile from the example data
197 shpfile = os.path.join(os.path.dirname(__file__),
198 os.pardir, "Data", "iceland", "political.shp")
199 layer = Layer("My Layer", session.OpenShapefile(shpfile))
200 proj = Projection(["proj=lcc", "ellps=clrk66",
201 "lat_1=0", "lat_2=20"],
202 "Layer Projection")
203 layer.SetProjection(proj)
204 map.AddLayer(layer)
205
206 filename = self.temp_file_name("save_layerproj.thuban")
207 save_session(session, filename)
208
209 file = open(filename)
210 written_contents = file.read()
211 file.close()
212 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
213 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
214 <session title="single map&amp;layer"
215 xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">
216 <fileshapesource id="D1"
217 filename="../../Data/iceland/political.shp"
218 filetype="shapefile"/>
219 <map title="Test Map">
220 <projection name="Unknown">
221 <parameter value="zone=26"/>
222 <parameter value="proj=utm"/>
223 <parameter value="ellps=clrk66"/>
224 </projection>
225 <layer title="My Layer" shapestore="D1" visible="true">
226 <projection name="Layer Projection">
227 <parameter value="proj=lcc"/>
228 <parameter value="ellps=clrk66"/>
229 <parameter value="lat_1=0"/>
230 <parameter value="lat_2=20"/>
231 </projection>
232 <classification>
233 <clnull label="">
234 <cldata fill="None" stroke="#000000"
235 stroke_width="1"/>
236 </clnull>
237 </classification>
238 </layer>
239 </map>
240 </session>'''
241 #print written_contents
242 #print "********************************************"
243 #print expected_contents
244 self.compare_xml(written_contents, expected_contents)
245
246 self.validate_data(written_contents)
247
248 def testRasterLayer(self):
249
250 MASK_NONE = RasterLayer.MASK_NONE
251 MASK_BIT = RasterLayer.MASK_BIT
252 MASK_ALPHA = RasterLayer.MASK_ALPHA
253
254 for opacity, masktype, opname, maskname in \
255 [(1, MASK_BIT, '', ''),
256 (.2, MASK_BIT, 'opacity="0.2"', ''),
257 (1, MASK_ALPHA, '', 'masktype="alpha"'),
258 (.5, MASK_ALPHA, 'opacity="0.5"', 'masktype="alpha"'),
259 (1, MASK_NONE, '', 'masktype="none"'),
260 (0, MASK_NONE, 'opacity="0"', 'masktype="none"') ]:
261
262
263 # deliberately put an apersand in the title :)
264 session = Session("single map&layer")
265 map = Map("Test Map")
266 session.AddMap(map)
267 # use shapefile from the example data
268 imgfile = os.path.join(os.path.dirname(__file__),
269 os.pardir, "Data", "iceland", "island.tif")
270 layer = RasterLayer("My RasterLayer", imgfile)
271
272 layer.SetOpacity(opacity)
273 layer.SetMaskType(masktype)
274
275 map.AddLayer(layer)
276
277 filename = self.temp_file_name("%s.thuban" % self.id())
278 save_session(session, filename)
279 session.Destroy()
280
281 file = open(filename)
282 written_contents = file.read()
283 file.close()
284 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
285 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
286 <session title="single map&amp;layer"
287 xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">
288 <map title="Test Map">
289 <rasterlayer title="My RasterLayer"
290 filename="../../Data/iceland/island.tif"
291 visible="true" %s %s>
292 </rasterlayer>
293 </map>
294 </session>''' % (opname, maskname)
295 #print written_contents
296 #print "********************************************"
297 #print expected_contents
298 self.compare_xml(written_contents, expected_contents)
299
300 self.validate_data(written_contents)
301
302 def testClassifiedLayer(self):
303 """Save a session with a single map with classifications"""
304 # deliberately put an apersand in the title :)
305 session = Session("Map with Classifications")
306 proj = Projection(["zone=26", "proj=utm", "ellps=clrk66"])
307 map = Map("Test Map", projection = proj)
308 session.AddMap(map)
309 # use shapefile from the example data
310 shpfile = os.path.join(os.path.dirname(__file__),
311 os.pardir, "Data", "iceland", "political.shp")
312 layer = Layer("My Layer", session.OpenShapefile(shpfile))
313 map.AddLayer(layer)
314 layer2 = Layer("My Layer", layer.ShapeStore())
315 map.AddLayer(layer2)
316
317 clazz = layer.GetClassification()
318
319 layer.SetClassificationColumn("AREA")
320
321 clazz.AppendGroup(ClassGroupSingleton(42, ClassGroupProperties(),
322 "single"))
323 clazz.AppendGroup(ClassGroupSingleton("text", ClassGroupProperties(),
324 "single-text"))
325
326 clazz.AppendGroup(ClassGroupRange((0, 42),
327 ClassGroupProperties(),
328 "range"))
329
330 range = ClassGroupRange(Range("[0;42]"))
331 range.SetProperties(ClassGroupProperties())
332 range.SetLabel("new-range")
333 clazz.AppendGroup(range)
334
335
336 clazz = layer2.GetClassification()
337 layer2.SetClassificationColumn("POPYCOUN")
338
339 # Classification with Latin 1 text
340 clazz.AppendGroup(ClassGroupSingleton(
341 internal_from_unicode(u'\xe4\xf6\xfc'), # ae, oe, ue
342 ClassGroupProperties(),
343 internal_from_unicode(u'\xdcml\xe4uts'))) # Uemlaeuts
344
345
346 filename = self.temp_file_name("%s.thuban" % self.id())
347 save_session(session, filename)
348
349 file = open(filename)
350 written_contents = file.read()
351 file.close()
352 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
353 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
354 <session title="Map with Classifications"
355 xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">
356 <fileshapesource id="D1"
357 filename="../../Data/iceland/political.shp"
358 filetype="shapefile"/>
359 <map title="Test Map">
360 <projection name="Unknown">
361 <parameter value="zone=26"/>
362 <parameter value="proj=utm"/>
363 <parameter value="ellps=clrk66"/>
364 </projection>
365 <layer title="My Layer" shapestore="D1" visible="true">
366 <classification field="AREA" field_type="double">
367 <clnull label="">
368 <cldata fill="None" stroke="#000000" stroke_width="1"/>
369 </clnull>
370 <clpoint value="42" label="single">
371 <cldata fill="None" stroke="#000000" stroke_width="1"/>
372 </clpoint>
373 <clpoint value="text" label="single-text">
374 <cldata fill="None" stroke="#000000" stroke_width="1"/>
375 </clpoint>
376 <clrange range="[0;42[" label="range">
377 <cldata fill="None" stroke="#000000" stroke_width="1"/>
378 </clrange>
379 <clrange range="[0;42]" label="new-range">
380 <cldata fill="None" stroke="#000000" stroke_width="1"/>
381 </clrange>
382 </classification>
383 </layer>
384 <layer title="My Layer" shapestore="D1" visible="true">
385 <classification field="POPYCOUN" field_type="string">
386 <clnull label="">
387 <cldata fill="None" stroke="#000000" stroke_width="1"/>
388 </clnull>
389 <clpoint value="\xc3\xa4\xc3\xb6\xc3\xbc"
390 label="\xc3\x9cml\xc3\xa4uts">
391 <cldata fill="None" stroke="#000000" stroke_width="1"/>
392 </clpoint>
393 </classification>
394 </layer>
395 </map>
396 </session>'''
397
398 #print written_contents
399 #print "********************************************"
400 #print expected_contents
401 self.compare_xml(written_contents, expected_contents)
402
403 self.validate_data(written_contents)
404
405 session.Destroy()
406
407 def test_dbf_table(self):
408 """Test saving a session with a dbf table link"""
409 session = self.session = Session("a DBF Table session")
410 # use shapefile from the example data
411 dbffile = os.path.join(os.path.dirname(__file__),
412 os.pardir, "Data", "iceland", "political.dbf")
413 table = session.AddTable(DBFTable(dbffile))
414
415 filename = self.temp_file_name("save_singletable.thuban")
416 save_session(session, filename)
417
418 file = open(filename)
419 written_contents = file.read()
420 file.close()
421 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
422 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
423 <session title="a DBF Table session"
424 xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">
425 <filetable id="D1" filename="../../Data/iceland/political.dbf"
426 filetype="DBF" title="political"/>
427 </session>'''
428
429 self.compare_xml(written_contents, expected_contents)
430 self.validate_data(written_contents)
431
432 def test_joined_table(self):
433 """Test saving a session with joined table"""
434 # Create a simple table to use in the join
435 dbffile = self.temp_file_name("save_joinedtable.dbf")
436 dbf = dbflib.create(dbffile)
437 dbf.add_field("RDTYPE", dbflib.FTInteger, 10, 0)
438 dbf.add_field("TEXT", dbflib.FTString, 10, 0)
439 dbf.write_record(0, {'RDTYPE': 8, "TEXT": "foo"})
440 dbf.write_record(1, {'RDTYPE': 2, "TEXT": "bar"})
441 dbf.write_record(2, {'RDTYPE': 3, "TEXT": "baz"})
442 dbf.close()
443
444 # Create the session and a map
445 session = Session("A Joined Table session")
446 try:
447 map = Map("Test Map")
448 session.AddMap(map)
449
450 # Add the dbf file to the session
451 dbftable = session.AddTable(DBFTable(dbffile))
452
453 # Create a layer with the shapefile to use in the join
454 shpfile = os.path.join(os.path.abspath(os.path.dirname(__file__)),
455 os.pardir, "Data", "iceland",
456 "roads-line.shp")
457 layer = Layer("My Layer", session.OpenShapefile(shpfile))
458 map.AddLayer(layer)
459
460 # Do the join
461 store = layer.ShapeStore()
462 #for col in store.Table().Columns():
463 # print col.name
464 joined = TransientJoinedTable(session.TransientDB(),
465 store.Table(), "RDLNTYPE",
466 dbftable, "RDTYPE",
467 outer_join = True)
468 store = session.AddShapeStore(DerivedShapeStore(store, joined))
469 layer.SetShapeStore(store)
470
471 # Save the session
472 filename = self.temp_file_name("save_joinedtable.thuban")
473 save_session(session, filename)
474
475 # Read it back and compare
476 file = open(filename)
477 written_contents = file.read()
478 file.close()
479 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
480 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
481 <session title="A Joined Table session"
482 xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">
483 <fileshapesource filename="../../Data/iceland/roads-line.shp"
484 filetype="shapefile" id="D142197204"/>
485 <filetable filename="save_joinedtable.dbf"
486 title="save_joinedtable"
487 filetype="DBF" id="D141881756"/>
488 <jointable id="D142180284"
489 title="Join of roads-line and save_joinedtable"
490 leftcolumn="RDLNTYPE" left="D142197204"
491 rightcolumn="RDTYPE" right="D141881756"
492 jointype="LEFT OUTER" />
493 <derivedshapesource id="D141915644"
494 table="D142180284"
495 shapesource="D142197204"/>
496 <map title="Test Map">
497 <layer title="My Layer"
498 shapestore="D141915644" visible="true">
499 <classification>
500 <clnull label="">
501 <cldata fill="None" stroke="#000000"
502 stroke_width="1"/>
503 </clnull>
504 </classification>
505 </layer>
506 </map>
507 </session>'''
508
509 self.compare_xml(written_contents, expected_contents)
510 self.validate_data(written_contents)
511 finally:
512 session.Destroy()
513 session = None
514
515
516 def test_save_postgis(self):
517 """Test saving a session with a postgis connection"""
518
519 class NonConnection(PostGISConnection):
520 """connection class that doesn't actually connect """
521 def connect(self):
522 pass
523
524 class NonConnectionStore(PostGISShapeStore):
525 """Shapestore that doesn't try to access the server"""
526 def _fetch_table_information(self):
527 # pretend that we've found a geometry column
528 self.geometry_column = "the_geom"
529 # pretend this is a ARC shape type.
530 self.shape_type = SHAPETYPE_ARC
531 def IDColumn(self):
532 """Return an object with a name attribute with value 'gid'"""
533 class dummycol:
534 name = "gid"
535 return dummycol
536
537 session = Session("A PostGIS Session")
538 try:
539 dbconn = NonConnection(dbname="plugh", host="xyzzy", port="42",
540 user="grue")
541 session.AddDBConnection(dbconn)
542 map = Map("Test Map")
543 session.AddMap(map)
544 store = NonConnectionStore(dbconn, "roads")
545 session.AddShapeStore(store)
546 layer = Layer("Roads to Nowhere", store)
547 map.AddLayer(layer)
548
549 # Save the session
550 filename = self.temp_file_name(self.id() + ".thuban")
551 save_session(session, filename)
552
553 # Read it back and compare
554 file = open(filename)
555 written = file.read()
556 file.close()
557 expected = '''<?xml version="1.0" encoding="UTF-8"?>
558 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
559 <session title="A PostGIS Session"
560 xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">
561 <dbconnection id="DB"
562 dbtype="postgis" dbname="plugh"
563 host="xyzzy" port="42"
564 user="grue"/>
565 <dbshapesource id="roads" dbconn="DB" tablename="roads"
566 id_column="gid" geometry_column="the_geom"/>
567 <map title="Test Map">
568 <layer title="Roads to Nowhere"
569 shapestore="roads" visible="true">
570 <classification>
571 <clnull label="">
572 <cldata fill="None" stroke="#000000"
573 stroke_width="1"/>
574 </clnull>
575 </classification>
576 </layer>
577 </map>
578 </session>'''
579 self.compare_xml(written, expected)
580 self.validate_data(written)
581 finally:
582 session.Destroy()
583
584
585 class MockDataStore:
586
587 """A very simple data store that only has dependencies"""
588
589 def __init__(self, name, *dependencies):
590 self.name = name
591 self.dependencies = dependencies
592
593 def __repr__(self):
594 return self.name
595
596 def Dependencies(self):
597 return self.dependencies
598
599
600 class TestStoreSort(unittest.TestCase):
601
602 def check_sort(self, containers, sorted):
603 """Check whether the list of data containers is sorted"""
604 # check whether sorted is in the right order
605 seen = {}
606 for container in sorted:
607 self.failIf(id(container) in seen,
608 "Container %r at least twice in %r" % (container,
609 sorted))
610 for dep in container.Dependencies():
611 self.assert_(id(dep) in seen,
612 "Dependency %r of %r not yet seen" % (dep,
613 container))
614 seen[id(container)] = 1
615 # check whether all of containers is in sorted
616 for container in containers:
617 self.assert_(id(container) in seen,
618 "Container %r in containers but not in sorted")
619 self.assertEquals(len(containers), len(sorted))
620
621 def test_sort_data_stores(self):
622 """Test Thuban.Model.save.sort_data_stores"""
623 d1 = MockDataStore("d1")
624 d2 = MockDataStore("d2")
625 d3 = MockDataStore("d3", d1)
626 d4 = MockDataStore("d4", d1, d3)
627
628 containers = [d4, d1, d2, d3]
629 self.check_sort(containers, sort_data_stores(containers))
630 containers = [d1, d3, d2, d4]
631 self.check_sort(containers, sort_data_stores(containers))
632
633
634
635 if __name__ == "__main__":
636 # Fake the __file__ global because it's needed by a test
637 import sys
638 __file__ = sys.argv[0]
639 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