PageRenderTime 150ms CodeModel.GetById 133ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/django/contrib/gis/tests/layermap/tests.py

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