/[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 2621 - (show annotations)
Fri May 6 14:19:40 2005 UTC (19 years, 10 months ago) by jonathan
Original Path: trunk/thuban/test/test_save.py
File MIME type: text/x-python
File size: 24854 byte(s)
(SaveSessionTest.testRasterLayer): Restructure test to cover a variety of
combinations of masktype and opacity settings.

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