Add Django-1.2.1
[frozenviper.git] / Django-1.2.1 / django / contrib / gis / tests / layermap / tests.py
blobf8239cb46d5bded6c2a116a46e9248ebe7532227
1 import os
2 import unittest
3 from decimal import Decimal
5 from django.utils.copycompat import copy
7 from django.contrib.gis.gdal import DataSource
8 from django.contrib.gis.tests.utils import mysql
9 from django.contrib.gis.utils.layermapping import LayerMapping, LayerMapError, InvalidDecimal, MissingForeignKey
11 from models import City, County, CountyFeat, Interstate, ICity1, ICity2, State, city_mapping, co_mapping, cofeat_mapping, inter_mapping
13 shp_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data'))
14 city_shp = os.path.join(shp_path, 'cities', 'cities.shp')
15 co_shp = os.path.join(shp_path, 'counties', 'counties.shp')
16 inter_shp = os.path.join(shp_path, 'interstates', 'interstates.shp')
18 # Dictionaries to hold what's expected in the county shapefile.
19 NAMES = ['Bexar', 'Galveston', 'Harris', 'Honolulu', 'Pueblo']
20 NUMS = [1, 2, 1, 19, 1] # Number of polygons for each.
21 STATES = ['Texas', 'Texas', 'Texas', 'Hawaii', 'Colorado']
23 class LayerMapTest(unittest.TestCase):
25 def test01_init(self):
26 "Testing LayerMapping initialization."
28 # Model field that does not exist.
29 bad1 = copy(city_mapping)
30 bad1['foobar'] = 'FooField'
32 # Shapefile field that does not exist.
33 bad2 = copy(city_mapping)
34 bad2['name'] = 'Nombre'
36 # Nonexistent geographic field type.
37 bad3 = copy(city_mapping)
38 bad3['point'] = 'CURVE'
40 # Incrementing through the bad mapping dictionaries and
41 # ensuring that a LayerMapError is raised.
42 for bad_map in (bad1, bad2, bad3):
43 try:
44 lm = LayerMapping(City, city_shp, bad_map)
45 except LayerMapError:
46 pass
47 else:
48 self.fail('Expected a LayerMapError.')
50 # A LookupError should be thrown for bogus encodings.
51 try:
52 lm = LayerMapping(City, city_shp, city_mapping, encoding='foobar')
53 except LookupError:
54 pass
55 else:
56 self.fail('Expected a LookupError')
58 def test02_simple_layermap(self):
59 "Test LayerMapping import of a simple point shapefile."
60 # Setting up for the LayerMapping.
61 lm = LayerMapping(City, city_shp, city_mapping)
62 lm.save()
64 # There should be three cities in the shape file.
65 self.assertEqual(3, City.objects.count())
67 # Opening up the shapefile, and verifying the values in each
68 # of the features made it to the model.
69 ds = DataSource(city_shp)
70 layer = ds[0]
71 for feat in layer:
72 city = City.objects.get(name=feat['Name'].value)
73 self.assertEqual(feat['Population'].value, city.population)
74 self.assertEqual(Decimal(str(feat['Density'])), city.density)
75 self.assertEqual(feat['Created'].value, city.dt)
77 # Comparing the geometries.
78 pnt1, pnt2 = feat.geom, city.point
79 self.assertAlmostEqual(pnt1.x, pnt2.x, 6)
80 self.assertAlmostEqual(pnt1.y, pnt2.y, 6)
82 def test03_layermap_strict(self):
83 "Testing the `strict` keyword, and import of a LineString shapefile."
84 # When the `strict` keyword is set an error encountered will force
85 # the importation to stop.
86 try:
87 lm = LayerMapping(Interstate, inter_shp, inter_mapping)
88 lm.save(silent=True, strict=True)
89 except InvalidDecimal:
90 # No transactions for geoms on MySQL; delete added features.
91 if mysql: Interstate.objects.all().delete()
92 else:
93 self.fail('Should have failed on strict import with invalid decimal values.')
95 # This LayerMapping should work b/c `strict` is not set.
96 lm = LayerMapping(Interstate, inter_shp, inter_mapping)
97 lm.save(silent=True)
99 # Two interstate should have imported correctly.
100 self.assertEqual(2, Interstate.objects.count())
102 # Verifying the values in the layer w/the model.
103 ds = DataSource(inter_shp)
105 # Only the first two features of this shapefile are valid.
106 valid_feats = ds[0][:2]
107 for feat in valid_feats:
108 istate = Interstate.objects.get(name=feat['Name'].value)
110 if feat.fid == 0:
111 self.assertEqual(Decimal(str(feat['Length'])), istate.length)
112 elif feat.fid == 1:
113 # Everything but the first two decimal digits were truncated,
114 # because the Interstate model's `length` field has decimal_places=2.
115 self.assertAlmostEqual(feat.get('Length'), float(istate.length), 2)
117 for p1, p2 in zip(feat.geom, istate.path):
118 self.assertAlmostEqual(p1[0], p2[0], 6)
119 self.assertAlmostEqual(p1[1], p2[1], 6)
121 def county_helper(self, county_feat=True):
122 "Helper function for ensuring the integrity of the mapped County models."
123 for name, n, st in zip(NAMES, NUMS, STATES):
124 # Should only be one record b/c of `unique` keyword.
125 c = County.objects.get(name=name)
126 self.assertEqual(n, len(c.mpoly))
127 self.assertEqual(st, c.state.name) # Checking ForeignKey mapping.
129 # Multiple records because `unique` was not set.
130 if county_feat:
131 qs = CountyFeat.objects.filter(name=name)
132 self.assertEqual(n, qs.count())
134 def test04_layermap_unique_multigeometry_fk(self):
135 "Testing the `unique`, and `transform`, geometry collection conversion, and ForeignKey mappings."
136 # All the following should work.
137 try:
138 # Telling LayerMapping that we want no transformations performed on the data.
139 lm = LayerMapping(County, co_shp, co_mapping, transform=False)
141 # Specifying the source spatial reference system via the `source_srs` keyword.
142 lm = LayerMapping(County, co_shp, co_mapping, source_srs=4269)
143 lm = LayerMapping(County, co_shp, co_mapping, source_srs='NAD83')
145 # Unique may take tuple or string parameters.
146 for arg in ('name', ('name', 'mpoly')):
147 lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique=arg)
148 except:
149 self.fail('No exception should be raised for proper use of keywords.')
151 # Testing invalid params for the `unique` keyword.
152 for e, arg in ((TypeError, 5.0), (ValueError, 'foobar'), (ValueError, ('name', 'mpolygon'))):
153 self.assertRaises(e, LayerMapping, County, co_shp, co_mapping, transform=False, unique=arg)
155 # No source reference system defined in the shapefile, should raise an error.
156 if not mysql:
157 self.assertRaises(LayerMapError, LayerMapping, County, co_shp, co_mapping)
159 # Passing in invalid ForeignKey mapping parameters -- must be a dictionary
160 # mapping for the model the ForeignKey points to.
161 bad_fk_map1 = copy(co_mapping); bad_fk_map1['state'] = 'name'
162 bad_fk_map2 = copy(co_mapping); bad_fk_map2['state'] = {'nombre' : 'State'}
163 self.assertRaises(TypeError, LayerMapping, County, co_shp, bad_fk_map1, transform=False)
164 self.assertRaises(LayerMapError, LayerMapping, County, co_shp, bad_fk_map2, transform=False)
166 # There exist no State models for the ForeignKey mapping to work -- should raise
167 # a MissingForeignKey exception (this error would be ignored if the `strict`
168 # keyword is not set).
169 lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name')
170 self.assertRaises(MissingForeignKey, lm.save, silent=True, strict=True)
172 # Now creating the state models so the ForeignKey mapping may work.
173 co, hi, tx = State(name='Colorado'), State(name='Hawaii'), State(name='Texas')
174 co.save(), hi.save(), tx.save()
176 # If a mapping is specified as a collection, all OGR fields that
177 # are not collections will be converted into them. For example,
178 # a Point column would be converted to MultiPoint. Other things being done
179 # w/the keyword args:
180 # `transform=False`: Specifies that no transform is to be done; this
181 # has the effect of ignoring the spatial reference check (because the
182 # county shapefile does not have implicit spatial reference info).
184 # `unique='name'`: Creates models on the condition that they have
185 # unique county names; geometries from each feature however will be
186 # appended to the geometry collection of the unique model. Thus,
187 # all of the various islands in Honolulu county will be in in one
188 # database record with a MULTIPOLYGON type.
189 lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name')
190 lm.save(silent=True, strict=True)
192 # A reference that doesn't use the unique keyword; a new database record will
193 # created for each polygon.
194 lm = LayerMapping(CountyFeat, co_shp, cofeat_mapping, transform=False)
195 lm.save(silent=True, strict=True)
197 # The county helper is called to ensure integrity of County models.
198 self.county_helper()
200 def test05_test_fid_range_step(self):
201 "Tests the `fid_range` keyword and the `step` keyword of .save()."
202 # Function for clearing out all the counties before testing.
203 def clear_counties(): County.objects.all().delete()
205 # Initializing the LayerMapping object to use in these tests.
206 lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name')
208 # Bad feature id ranges should raise a type error.
209 clear_counties()
210 bad_ranges = (5.0, 'foo', co_shp)
211 for bad in bad_ranges:
212 self.assertRaises(TypeError, lm.save, fid_range=bad)
214 # Step keyword should not be allowed w/`fid_range`.
215 fr = (3, 5) # layer[3:5]
216 self.assertRaises(LayerMapError, lm.save, fid_range=fr, step=10)
217 lm.save(fid_range=fr)
219 # Features IDs 3 & 4 are for Galveston County, Texas -- only
220 # one model is returned because the `unique` keyword was set.
221 qs = County.objects.all()
222 self.assertEqual(1, qs.count())
223 self.assertEqual('Galveston', qs[0].name)
225 # Features IDs 5 and beyond for Honolulu County, Hawaii, and
226 # FID 0 is for Pueblo County, Colorado.
227 clear_counties()
228 lm.save(fid_range=slice(5, None), silent=True, strict=True) # layer[5:]
229 lm.save(fid_range=slice(None, 1), silent=True, strict=True) # layer[:1]
231 # Only Pueblo & Honolulu counties should be present because of
232 # the `unique` keyword. Have to set `order_by` on this QuerySet
233 # or else MySQL will return a different ordering than the other dbs.
234 qs = County.objects.order_by('name')
235 self.assertEqual(2, qs.count())
236 hi, co = tuple(qs)
237 hi_idx, co_idx = tuple(map(NAMES.index, ('Honolulu', 'Pueblo')))
238 self.assertEqual('Pueblo', co.name); self.assertEqual(NUMS[co_idx], len(co.mpoly))
239 self.assertEqual('Honolulu', hi.name); self.assertEqual(NUMS[hi_idx], len(hi.mpoly))
241 # Testing the `step` keyword -- should get the same counties
242 # regardless of we use a step that divides equally, that is odd,
243 # or that is larger than the dataset.
244 for st in (4,7,1000):
245 clear_counties()
246 lm.save(step=st, strict=True)
247 self.county_helper(county_feat=False)
249 def test06_model_inheritance(self):
250 "Tests LayerMapping on inherited models. See #12093."
251 icity_mapping = {'name' : 'Name',
252 'population' : 'Population',
253 'density' : 'Density',
254 'point' : 'POINT',
255 'dt' : 'Created',
258 # Parent model has geometry field.
259 lm1 = LayerMapping(ICity1, city_shp, icity_mapping)
260 lm1.save()
262 # Grandparent has geometry field.
263 lm2 = LayerMapping(ICity2, city_shp, icity_mapping)
264 lm2.save()
266 self.assertEqual(6, ICity1.objects.count())
267 self.assertEqual(3, ICity2.objects.count())
269 def suite():
270 s = unittest.TestSuite()
271 s.addTest(unittest.makeSuite(LayerMapTest))
272 return s