/django/contrib/gis/gdal/tests/test_ds.py
Python | 237 lines | 146 code | 42 blank | 49 comment | 51 complexity | a89f4c74c94b4b6dced9973a48f180d1 MD5 | raw file
1import os 2import unittest 3from django.contrib.gis.gdal import DataSource, Envelope, OGRGeometry, OGRException, OGRIndexError, GDAL_VERSION 4from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString 5from django.contrib.gis.geometry.test_data import get_ds_file, TestDS, TEST_DATA 6 7# List of acceptable data sources. 8ds_list = (TestDS('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='ESRI Shapefile', 9 fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,}, 10 extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS 11 srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]', 12 field_values={'dbl' : [float(i) for i in range(1, 6)], 'int' : range(1, 6), 'str' : [str(i) for i in range(1, 6)]}, 13 fids=range(5)), 14 TestDS('test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype='Point25D', driver='VRT', 15 fields={'POINT_X' : OFTString, 'POINT_Y' : OFTString, 'NUM' : OFTString}, # VRT uses CSV, which all types are OFTString. 16 extent=(1.0, 2.0, 100.0, 523.5), # Min/Max from CSV 17 field_values={'POINT_X' : ['1.0', '5.0', '100.0'], 'POINT_Y' : ['2.0', '23.0', '523.5'], 'NUM' : ['5', '17', '23']}, 18 fids=range(1,4)), 19 TestDS('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3, 20 driver='ESRI Shapefile', 21 fields={'float' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,}, 22 extent=(-1.01513,-0.558245,0.161876,0.839637), # Got extent from QGIS 23 srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'), 24 ) 25 26bad_ds = (TestDS('foo'), 27 ) 28 29class DataSourceTest(unittest.TestCase): 30 31 def test01_valid_shp(self): 32 "Testing valid SHP Data Source files." 33 34 for source in ds_list: 35 # Loading up the data source 36 ds = DataSource(source.ds) 37 38 # Making sure the layer count is what's expected (only 1 layer in a SHP file) 39 self.assertEqual(1, len(ds)) 40 41 # Making sure GetName works 42 self.assertEqual(source.ds, ds.name) 43 44 # Making sure the driver name matches up 45 self.assertEqual(source.driver, str(ds.driver)) 46 47 # Making sure indexing works 48 try: 49 ds[len(ds)] 50 except OGRIndexError: 51 pass 52 else: 53 self.fail('Expected an IndexError!') 54 55 def test02_invalid_shp(self): 56 "Testing invalid SHP files for the Data Source." 57 for source in bad_ds: 58 self.assertRaises(OGRException, DataSource, source.ds) 59 60 def test03a_layers(self): 61 "Testing Data Source Layers." 62 print "\nBEGIN - expecting out of range feature id error; safe to ignore.\n" 63 for source in ds_list: 64 ds = DataSource(source.ds) 65 66 # Incrementing through each layer, this tests DataSource.__iter__ 67 for layer in ds: 68 # Making sure we get the number of features we expect 69 self.assertEqual(len(layer), source.nfeat) 70 71 # Making sure we get the number of fields we expect 72 self.assertEqual(source.nfld, layer.num_fields) 73 self.assertEqual(source.nfld, len(layer.fields)) 74 75 # Testing the layer's extent (an Envelope), and it's properties 76 if source.driver == 'VRT' and (GDAL_VERSION >= (1, 7, 0) and GDAL_VERSION < (1, 7, 3)): 77 # There's a known GDAL regression with retrieving the extent 78 # of a VRT layer in versions 1.7.0-1.7.2: 79 # http://trac.osgeo.org/gdal/ticket/3783 80 pass 81 else: 82 self.assertEqual(True, isinstance(layer.extent, Envelope)) 83 self.assertAlmostEqual(source.extent[0], layer.extent.min_x, 5) 84 self.assertAlmostEqual(source.extent[1], layer.extent.min_y, 5) 85 self.assertAlmostEqual(source.extent[2], layer.extent.max_x, 5) 86 self.assertAlmostEqual(source.extent[3], layer.extent.max_y, 5) 87 88 # Now checking the field names. 89 flds = layer.fields 90 for f in flds: self.assertEqual(True, f in source.fields) 91 92 # Negative FIDs are not allowed. 93 self.assertRaises(OGRIndexError, layer.__getitem__, -1) 94 self.assertRaises(OGRIndexError, layer.__getitem__, 50000) 95 96 if hasattr(source, 'field_values'): 97 fld_names = source.field_values.keys() 98 99 # Testing `Layer.get_fields` (which uses Layer.__iter__) 100 for fld_name in fld_names: 101 self.assertEqual(source.field_values[fld_name], layer.get_fields(fld_name)) 102 103 # Testing `Layer.__getitem__`. 104 for i, fid in enumerate(source.fids): 105 feat = layer[fid] 106 self.assertEqual(fid, feat.fid) 107 # Maybe this should be in the test below, but we might as well test 108 # the feature values here while in this loop. 109 for fld_name in fld_names: 110 self.assertEqual(source.field_values[fld_name][i], feat.get(fld_name)) 111 print "\nEND - expecting out of range feature id error; safe to ignore." 112 113 def test03b_layer_slice(self): 114 "Test indexing and slicing on Layers." 115 # Using the first data-source because the same slice 116 # can be used for both the layer and the control values. 117 source = ds_list[0] 118 ds = DataSource(source.ds) 119 120 sl = slice(1, 3) 121 feats = ds[0][sl] 122 123 for fld_name in ds[0].fields: 124 test_vals = [feat.get(fld_name) for feat in feats] 125 control_vals = source.field_values[fld_name][sl] 126 self.assertEqual(control_vals, test_vals) 127 128 def test03c_layer_references(self): 129 "Test to make sure Layer access is still available without the DataSource." 130 source = ds_list[0] 131 132 # See ticket #9448. 133 def get_layer(): 134 # This DataSource object is not accessible outside this 135 # scope. However, a reference should still be kept alive 136 # on the `Layer` returned. 137 ds = DataSource(source.ds) 138 return ds[0] 139 140 # Making sure we can call OGR routines on the Layer returned. 141 lyr = get_layer() 142 self.assertEqual(source.nfeat, len(lyr)) 143 self.assertEqual(source.gtype, lyr.geom_type.num) 144 145 def test04_features(self): 146 "Testing Data Source Features." 147 for source in ds_list: 148 ds = DataSource(source.ds) 149 150 # Incrementing through each layer 151 for layer in ds: 152 # Incrementing through each feature in the layer 153 for feat in layer: 154 # Making sure the number of fields, and the geometry type 155 # are what's expected. 156 self.assertEqual(source.nfld, len(list(feat))) 157 self.assertEqual(source.gtype, feat.geom_type) 158 159 # Making sure the fields match to an appropriate OFT type. 160 for k, v in source.fields.items(): 161 # Making sure we get the proper OGR Field instance, using 162 # a string value index for the feature. 163 self.assertEqual(True, isinstance(feat[k], v)) 164 165 # Testing Feature.__iter__ 166 for fld in feat: self.assertEqual(True, fld.name in source.fields.keys()) 167 168 def test05_geometries(self): 169 "Testing Geometries from Data Source Features." 170 for source in ds_list: 171 ds = DataSource(source.ds) 172 173 # Incrementing through each layer and feature. 174 for layer in ds: 175 for feat in layer: 176 g = feat.geom 177 178 # Making sure we get the right Geometry name & type 179 self.assertEqual(source.geom, g.geom_name) 180 self.assertEqual(source.gtype, g.geom_type) 181 182 # Making sure the SpatialReference is as expected. 183 if hasattr(source, 'srs_wkt'): 184 self.assertEqual(source.srs_wkt, g.srs.wkt) 185 186 def test06_spatial_filter(self): 187 "Testing the Layer.spatial_filter property." 188 ds = DataSource(get_ds_file('cities', 'shp')) 189 lyr = ds[0] 190 191 # When not set, it should be None. 192 self.assertEqual(None, lyr.spatial_filter) 193 194 # Must be set a/an OGRGeometry or 4-tuple. 195 self.assertRaises(TypeError, lyr._set_spatial_filter, 'foo') 196 197 # Setting the spatial filter with a tuple/list with the extent of 198 # a buffer centering around Pueblo. 199 self.assertRaises(ValueError, lyr._set_spatial_filter, range(5)) 200 filter_extent = (-105.609252, 37.255001, -103.609252, 39.255001) 201 lyr.spatial_filter = (-105.609252, 37.255001, -103.609252, 39.255001) 202 self.assertEqual(OGRGeometry.from_bbox(filter_extent), lyr.spatial_filter) 203 feats = [feat for feat in lyr] 204 self.assertEqual(1, len(feats)) 205 self.assertEqual('Pueblo', feats[0].get('Name')) 206 207 # Setting the spatial filter with an OGRGeometry for buffer centering 208 # around Houston. 209 filter_geom = OGRGeometry('POLYGON((-96.363151 28.763374,-94.363151 28.763374,-94.363151 30.763374,-96.363151 30.763374,-96.363151 28.763374))') 210 lyr.spatial_filter = filter_geom 211 self.assertEqual(filter_geom, lyr.spatial_filter) 212 feats = [feat for feat in lyr] 213 self.assertEqual(1, len(feats)) 214 self.assertEqual('Houston', feats[0].get('Name')) 215 216 # Clearing the spatial filter by setting it to None. Now 217 # should indicate that there are 3 features in the Layer. 218 lyr.spatial_filter = None 219 self.assertEqual(3, len(lyr)) 220 221 def test07_integer_overflow(self): 222 "Testing that OFTReal fields, treated as OFTInteger, do not overflow." 223 # Using *.dbf from Census 2010 TIGER Shapefile for Texas, 224 # which has land area ('ALAND10') stored in a Real field 225 # with no precision. 226 ds = DataSource(os.path.join(TEST_DATA, 'texas.dbf')) 227 feat = ds[0][0] 228 # Reference value obtained using `ogrinfo`. 229 self.assertEqual(676586997978, feat.get('ALAND10')) 230 231def suite(): 232 s = unittest.TestSuite() 233 s.addTest(unittest.makeSuite(DataSourceTest)) 234 return s 235 236def run(verbosity=2): 237 unittest.TextTestRunner(verbosity=verbosity).run(suite())