/[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 1638 - (show annotations)
Fri Aug 22 18:19:14 2003 UTC (21 years, 6 months ago) by bh
Original Path: trunk/thuban/test/test_save.py
File MIME type: text/x-python
File size: 23440 byte(s)
Implement saving a session with a postgis connection

* Resources/XML/thuban-0.9.dtd (dbconnection, dbshapesource) New
elements for database connections and shapestores using db
connections
(session): Add the dbconnections to the content model

* Thuban/Model/save.py (SessionSaver.write_db_connections): New.
Write the db connections
(SessionSaver.write_session): Call write_db_connections to write
the connection before the data sources
(SessionSaver.write_data_containers): Handle postgis shapestores

* test/test_save.py (SaveSessionTest.thubanids)
(SaveSessionTest.thubanidrefs): Update for new DTD
(SaveSessionTest.test_save_postgis): New. Test saving a session
with postgis connections

* Thuban/Model/postgisdb.py (PostGISTable.DBConnection)
(PostGISTable.TableName): New accessor methods for the connection
and table name

* test/test_postgis_db.py (TestPostGISTable.test_dbconn)
(TestPostGISTable.test_dbname): New methods to test the new
PostGISConnection methods

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