/[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 2734 - (show annotations)
Thu Mar 1 12:42:59 2007 UTC (18 years ago) by bramz
File MIME type: text/x-python
File size: 26086 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 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 ClassGroupPattern, 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 # Pattern
346 clazz.AppendGroup(ClassGroupPattern("BUI", ClassGroupProperties(),
347 "pattern"))
348
349 filename = self.temp_file_name("%s.thuban" % self.id())
350 save_session(session, filename)
351
352 file = open(filename)
353 written_contents = file.read()
354 file.close()
355 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
356 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
357 <session title="Map with Classifications"
358 xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">
359 <fileshapesource id="D1"
360 filename="../../Data/iceland/political.shp"
361 filetype="shapefile"/>
362 <map title="Test Map">
363 <projection name="Unknown">
364 <parameter value="zone=26"/>
365 <parameter value="proj=utm"/>
366 <parameter value="ellps=clrk66"/>
367 </projection>
368 <layer title="My Layer" shapestore="D1" visible="true">
369 <classification field="AREA" field_type="double">
370 <clnull label="">
371 <cldata fill="None" stroke="#000000" stroke_width="1"/>
372 </clnull>
373 <clpoint value="42" label="single">
374 <cldata fill="None" stroke="#000000" stroke_width="1"/>
375 </clpoint>
376 <clpoint value="text" label="single-text">
377 <cldata fill="None" stroke="#000000" stroke_width="1"/>
378 </clpoint>
379 <clrange range="[0;42[" label="range">
380 <cldata fill="None" stroke="#000000" stroke_width="1"/>
381 </clrange>
382 <clrange range="[0;42]" label="new-range">
383 <cldata fill="None" stroke="#000000" stroke_width="1"/>
384 </clrange>
385 </classification>
386 </layer>
387 <layer title="My Layer" shapestore="D1" visible="true">
388 <classification field="POPYCOUN" field_type="string">
389 <clnull label="">
390 <cldata fill="None" stroke="#000000" stroke_width="1"/>
391 </clnull>
392 <clpoint value="\xc3\xa4\xc3\xb6\xc3\xbc"
393 label="\xc3\x9cml\xc3\xa4uts">
394 <cldata fill="None" stroke="#000000" stroke_width="1"/>
395 </clpoint>
396 <clpattern pattern="BUI" label="pattern">
397 <cldata fill="None" stroke="#000000" stroke_width="1"/>
398 </clpattern>
399 </classification>
400 </layer>
401 </map>
402 </session>'''
403
404 #print written_contents
405 #print "********************************************"
406 #print expected_contents
407 self.compare_xml(written_contents, expected_contents)
408
409 self.validate_data(written_contents)
410
411 session.Destroy()
412
413 def test_dbf_table(self):
414 """Test saving a session with a dbf table link"""
415 session = self.session = Session("a DBF Table session")
416 # use shapefile from the example data
417 dbffile = os.path.join(os.path.dirname(__file__),
418 os.pardir, "Data", "iceland", "political.dbf")
419 table = session.AddTable(DBFTable(dbffile))
420
421 filename = self.temp_file_name("save_singletable.thuban")
422 save_session(session, filename)
423
424 file = open(filename)
425 written_contents = file.read()
426 file.close()
427 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
428 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
429 <session title="a DBF Table session"
430 xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">
431 <filetable id="D1" filename="../../Data/iceland/political.dbf"
432 filetype="DBF" title="political"/>
433 </session>'''
434
435 self.compare_xml(written_contents, expected_contents)
436 self.validate_data(written_contents)
437
438 def test_joined_table(self):
439 """Test saving a session with joined table"""
440 # Create a simple table to use in the join
441 dbffile = self.temp_file_name("save_joinedtable.dbf")
442 dbf = dbflib.create(dbffile)
443 dbf.add_field("RDTYPE", dbflib.FTInteger, 10, 0)
444 dbf.add_field("TEXT", dbflib.FTString, 10, 0)
445 dbf.write_record(0, {'RDTYPE': 8, "TEXT": "foo"})
446 dbf.write_record(1, {'RDTYPE': 2, "TEXT": "bar"})
447 dbf.write_record(2, {'RDTYPE': 3, "TEXT": "baz"})
448 dbf.close()
449
450 # Create the session and a map
451 session = Session("A Joined Table session")
452 try:
453 map = Map("Test Map")
454 session.AddMap(map)
455
456 # Add the dbf file to the session
457 dbftable = session.AddTable(DBFTable(dbffile))
458
459 # Create a layer with the shapefile to use in the join
460 shpfile = os.path.join(os.path.abspath(os.path.dirname(__file__)),
461 os.pardir, "Data", "iceland",
462 "roads-line.shp")
463 layer = Layer("My Layer", session.OpenShapefile(shpfile))
464 map.AddLayer(layer)
465
466 # Do the join
467 store = layer.ShapeStore()
468 #for col in store.Table().Columns():
469 # print col.name
470 joined = TransientJoinedTable(session.TransientDB(),
471 store.Table(), "RDLNTYPE",
472 dbftable, "RDTYPE",
473 outer_join = True)
474 store = session.AddShapeStore(DerivedShapeStore(store, joined))
475 layer.SetShapeStore(store)
476
477 # Save the session
478 filename = self.temp_file_name("save_joinedtable.thuban")
479 save_session(session, filename)
480
481 # Read it back and compare
482 file = open(filename)
483 written_contents = file.read()
484 file.close()
485 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
486 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
487 <session title="A Joined Table session"
488 xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">
489 <fileshapesource filename="../../Data/iceland/roads-line.shp"
490 filetype="shapefile" id="D142197204"/>
491 <filetable filename="save_joinedtable.dbf"
492 title="save_joinedtable"
493 filetype="DBF" id="D141881756"/>
494 <jointable id="D142180284"
495 title="Join of roads-line and save_joinedtable"
496 leftcolumn="RDLNTYPE" left="D142197204"
497 rightcolumn="RDTYPE" right="D141881756"
498 jointype="LEFT OUTER" />
499 <derivedshapesource id="D141915644"
500 table="D142180284"
501 shapesource="D142197204"/>
502 <map title="Test Map">
503 <layer title="My Layer"
504 shapestore="D141915644" visible="true">
505 <classification>
506 <clnull label="">
507 <cldata fill="None" stroke="#000000"
508 stroke_width="1"/>
509 </clnull>
510 </classification>
511 </layer>
512 </map>
513 </session>'''
514
515 self.compare_xml(written_contents, expected_contents)
516 self.validate_data(written_contents)
517 finally:
518 session.Destroy()
519 session = None
520
521
522 def test_save_postgis(self):
523 """Test saving a session with a postgis connection"""
524
525 class NonConnection(PostGISConnection):
526 """connection class that doesn't actually connect """
527 def connect(self):
528 pass
529
530 class NonConnectionStore(PostGISShapeStore):
531 """Shapestore that doesn't try to access the server"""
532 def _fetch_table_information(self):
533 # pretend that we've found a geometry column
534 self.geometry_column = "the_geom"
535 # pretend this is a ARC shape type.
536 self.shape_type = SHAPETYPE_ARC
537 def IDColumn(self):
538 """Return an object with a name attribute with value 'gid'"""
539 class dummycol:
540 name = "gid"
541 return dummycol
542
543 session = Session("A PostGIS Session")
544 try:
545 dbconn = NonConnection(dbname="plugh", host="xyzzy", port="42",
546 user="grue")
547 session.AddDBConnection(dbconn)
548 map = Map("Test Map")
549 session.AddMap(map)
550 store = NonConnectionStore(dbconn, "roads")
551 session.AddShapeStore(store)
552 layer = Layer("Roads to Nowhere", store)
553 map.AddLayer(layer)
554
555 # Save the session
556 filename = self.temp_file_name(self.id() + ".thuban")
557 save_session(session, filename)
558
559 # Read it back and compare
560 file = open(filename)
561 written = file.read()
562 file.close()
563 expected = '''<?xml version="1.0" encoding="UTF-8"?>
564 <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
565 <session title="A PostGIS Session"
566 xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd">
567 <dbconnection id="DB"
568 dbtype="postgis" dbname="plugh"
569 host="xyzzy" port="42"
570 user="grue"/>
571 <dbshapesource id="roads" dbconn="DB" tablename="roads"
572 id_column="gid" geometry_column="the_geom"/>
573 <map title="Test Map">
574 <layer title="Roads to Nowhere"
575 shapestore="roads" visible="true">
576 <classification>
577 <clnull label="">
578 <cldata fill="None" stroke="#000000"
579 stroke_width="1"/>
580 </clnull>
581 </classification>
582 </layer>
583 </map>
584 </session>'''
585 self.compare_xml(written, expected)
586 self.validate_data(written)
587 finally:
588 session.Destroy()
589
590
591 class MockDataStore:
592
593 """A very simple data store that only has dependencies"""
594
595 def __init__(self, name, *dependencies):
596 self.name = name
597 self.dependencies = dependencies
598
599 def __repr__(self):
600 return self.name
601
602 def Dependencies(self):
603 return self.dependencies
604
605
606 class TestStoreSort(unittest.TestCase):
607
608 def check_sort(self, containers, sorted):
609 """Check whether the list of data containers is sorted"""
610 # check whether sorted is in the right order
611 seen = {}
612 for container in sorted:
613 self.failIf(id(container) in seen,
614 "Container %r at least twice in %r" % (container,
615 sorted))
616 for dep in container.Dependencies():
617 self.assert_(id(dep) in seen,
618 "Dependency %r of %r not yet seen" % (dep,
619 container))
620 seen[id(container)] = 1
621 # check whether all of containers is in sorted
622 for container in containers:
623 self.assert_(id(container) in seen,
624 "Container %r in containers but not in sorted")
625 self.assertEquals(len(containers), len(sorted))
626
627 def test_sort_data_stores(self):
628 """Test Thuban.Model.save.sort_data_stores"""
629 d1 = MockDataStore("d1")
630 d2 = MockDataStore("d2")
631 d3 = MockDataStore("d3", d1)
632 d4 = MockDataStore("d4", d1, d3)
633
634 containers = [d4, d1, d2, d3]
635 self.check_sort(containers, sort_data_stores(containers))
636 containers = [d1, d3, d2, d4]
637 self.check_sort(containers, sort_data_stores(containers))
638
639
640
641 if __name__ == "__main__":
642 # Fake the __file__ global because it's needed by a test
643 import sys
644 __file__ = sys.argv[0]
645 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