Django

Code

Changeset 8034

Show
Ignore:
Timestamp:
07/21/08 20:10:59 (5 months ago)
Author:
jbronn
Message:

gis: gdal: Features may now be fetched from OGR layers that do not support random access reading, but no more negative indexes are allowed; cleaned up OGRGeomType; moved test vector data into its own directory.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/gis/django/contrib/gis/gdal/error.py

    r6686 r8034  
    2626                6 : (OGRException, 'OGR failure.'), 
    2727                7 : (SRSException, 'Unsupported SRS.'), 
     28                8 : (OGRException, 'Invalid handle.'), 
    2829                } 
    2930OGRERR_NONE = 0 
  • django/branches/gis/django/contrib/gis/gdal/geometries.py

    r7767 r8034  
    202202    def geom_type(self): 
    203203        "Returns the Type for this Geometry." 
    204         return OGRGeomType(get_geom_type(self._ptr)) 
     204        try: 
     205            return OGRGeomType(get_geom_type(self._ptr)) 
     206        except OGRException: 
     207            # VRT datasources return an invalid geometry type 
     208            # number, but a valid name -- we'll try that instead. 
     209            # See: http://trac.osgeo.org/gdal/ticket/2491 
     210            return OGRGeomType(get_geom_name(self._ptr)) 
    205211 
    206212    @property 
  • django/branches/gis/django/contrib/gis/gdal/geomtype.py

    r7665 r8034  
    55    "Encapulates OGR Geometry Types." 
    66 
    7     # Ordered array of acceptable strings and their corresponding OGRwkbGeometryType 
    8     __ogr_str = ['Unknown', 'Point', 'LineString', 'Polygon', 'MultiPoint', 
    9                  'MultiLineString', 'MultiPolygon', 'GeometryCollection', 
    10                  'LinearRing'] 
    11     __ogr_int = [0, 1, 2, 3, 4, 5, 6, 7, 101] 
     7    # Dictionary of acceptable OGRwkbGeometryType s and their string names. 
     8    _types = {0 : 'Unknown', 
     9              1 : 'Point', 
     10              2 : 'LineString', 
     11              3 : 'Polygon', 
     12              4 : 'MultiPoint', 
     13              5 : 'MultiLineString', 
     14              6 : 'MultiPolygon', 
     15              7 : 'GeometryCollection', 
     16              100 : 'None', 
     17              101 : 'LinearRing', 
     18              } 
     19    # Reverse type dictionary, keyed by lower-case of the name. 
     20    _str_types = dict([(v.lower(), k) for k, v in _types.items()]) 
    1221 
    1322    def __init__(self, type_input): 
    1423        "Figures out the correct OGR Type based upon the input." 
    1524        if isinstance(type_input, OGRGeomType): 
    16             self._index = type_input._index 
     25            num = type_input.num 
    1726        elif isinstance(type_input, basestring): 
    18             idx = self._has_str(self.__ogr_str, type_input
    19             if idx == None: 
     27            num = self._str_types.get(type_input.lower(), None
     28            if num is None: 
    2029                raise OGRException('Invalid OGR String Type "%s"' % type_input) 
    21             self._index = idx 
    2230        elif isinstance(type_input, int): 
    23             if not type_input in self.__ogr_int
     31            if not type_input in self._types
    2432                raise OGRException('Invalid OGR Integer Type: %d' % type_input) 
    25             self._index =  self.__ogr_int.index(type_input) 
     33            num = type_input 
    2634        else: 
    2735            raise TypeError('Invalid OGR input type given.') 
     36         
     37        # Setting the OGR geometry type number. 
     38        self.num = num 
    2839 
    2940    def __str__(self): 
    30         "Returns a short-hand string form of the OGR Geometry type." 
    31         return self.__ogr_str[self._index] 
     41        "Returns the value of the name property." 
     42        return self.name 
    3243 
    3344    def __eq__(self, other): 
     
    3748        """ 
    3849        if isinstance(other, OGRGeomType): 
    39             return self._index == other._index 
     50            return self.num == other.num 
    4051        elif isinstance(other, basestring): 
    41             idx = self._has_str(self.__ogr_str, other) 
    42             if not (idx == None): return self._index == idx 
     52            return self.name.lower() == other.lower() 
     53        elif isinstance(other, int): 
     54            return self.num == other 
     55        else: 
    4356            return False 
    44         elif isinstance(other, int): 
    45             if not other in self.__ogr_int: return False 
    46             return self.__ogr_int.index(other) == self._index 
    47         else: 
    48             raise TypeError('Cannot compare with type: %s' % str(type(other))) 
    4957 
    5058    def __ne__(self, other): 
    5159        return not (self == other) 
    5260 
    53     def _has_str(self, arr, s): 
    54         "Case-insensitive search of the string array for the given pattern." 
    55         s_low = s.lower() 
    56         for i in xrange(len(arr)): 
    57             if s_low == arr[i].lower(): return i 
    58         return None 
     61    @property 
     62    def name(self): 
     63        "Returns a short-hand string form of the OGR Geometry type." 
     64        return self._types[self.num] 
    5965 
    6066    @property 
    6167    def django(self): 
    6268        "Returns the Django GeometryField for this OGR Type." 
    63         s = self.__ogr_str[self._index] 
    64         if s in ('Unknown', 'LinearRing'): 
     69        s = self.name 
     70        if s in ('Unknown', 'LinearRing', 'None'): 
    6571            return None 
    6672        else: 
    6773            return s + 'Field' 
    68  
    69     @property 
    70     def num(self): 
    71         "Returns the OGRwkbGeometryType number for the OGR Type." 
    72         return self.__ogr_int[self._index] 
  • django/branches/gis/django/contrib/gis/gdal/layer.py

    r6992 r8034  
    1515    get_field_count, get_field_defn, get_field_name, get_field_precision, \ 
    1616    get_field_width, get_field_type, get_layer_defn, get_layer_srs, \ 
    17     get_next_feature, reset_reading 
     17    get_next_feature, reset_reading, test_capability 
    1818from django.contrib.gis.gdal.prototypes.srs import clone_srs 
    1919 
     
    3333        self._ptr = layer_ptr 
    3434        self._ldefn = get_layer_defn(self._ptr) 
     35        # Does the Layer support random reading? 
     36        self._random_read = self.test_capability('RandomRead') 
    3537 
    3638    def __getitem__(self, index): 
    3739        "Gets the Feature at the specified index." 
    38         if not isinstance(index, (slice, int)): 
    39             raise TypeError 
    40         end = self.num_feat 
    41         if isinstance(index,int): 
    42             # An integer index was given 
    43             if index < 0: 
    44                 index = end - index 
    45             if index < 0 or index >= self.num_feat: 
    46                 raise OGRIndexError('index out of range') 
     40        if isinstance(index, (int, long)): 
     41            # An integer index was given -- we cannot do a check based on the 
     42            # number of features because the beginning and ending feature IDs 
     43            # are not guaranteed to be 0 and len(layer)-1, respectively. 
     44            if index < 0: raise OGRIndexError('Negative indices are not allowed on OGR Layers.') 
    4745            return self._make_feature(index) 
    48         else:  
     46        elif isinstance(index, slice): 
    4947            # A slice was given 
    50             start, stop, stride = index.indices(end) 
    51             return [self._make_feature(offset) for offset in range(start,stop,stride)] 
     48            start, stop, stride = index.indices(self.num_feat) 
     49            return [self._make_feature(fid) for fid in xrange(start, stop, stride)] 
     50        else: 
     51            raise TypeError('Integers and slices may only be used when indexing OGR Layers.') 
    5252 
    5353    def __iter__(self): 
     
    5555        # ResetReading() must be called before iteration is to begin. 
    5656        reset_reading(self._ptr) 
    57         for i in range(self.num_feat): 
     57        for i in xrange(self.num_feat): 
    5858            yield Feature(get_next_feature(self._ptr), self._ldefn) 
    5959 
     
    6666        return self.name 
    6767 
    68     def _make_feature(self, offset): 
    69         "Helper routine for __getitem__ that makes a feature from an offset." 
    70         return Feature(get_feature(self._ptr, offset), self._ldefn) 
     68    def _make_feature(self, feat_id): 
     69        """ 
     70        Helper routine for __getitem__ that constructs a Feature from the given 
     71        Feature ID.  If the OGR Layer does not support random-access reading, 
     72        then each feature of the layer will be incremented through until the 
     73        a Feature is found matching the given feature ID. 
     74        """ 
     75        if self._random_read: 
     76            # If the Layer supports random reading, return. 
     77            try: 
     78                return Feature(get_feature(self._ptr, feat_id), self._ldefn) 
     79            except OGRException: 
     80                pass 
     81        else: 
     82            # Random access isn't supported, have to increment through 
     83            # each feature until the given feature ID is encountered. 
     84            for feat in self: 
     85                if feat.fid == feat_id: return feat 
     86        # Should have returned a Feature, raise an OGRIndexError.     
     87        raise OGRIndexError('Invalid feature id: %s.' % feat_id) 
    7188 
    7289    #### Layer properties #### 
     
    159176        else: 
    160177            return [feat.geom for feat in self] 
     178 
     179    def test_capability(self, capability): 
     180        """ 
     181        Returns a bool indicating whether the this Layer supports the given 
     182        capability (a string).  Valid capability strings include: 
     183          'RandomRead', 'SequentialWrite', 'RandomWrite', 'FastSpatialFilter', 
     184          'FastFeatureCount', 'FastGetExtent', 'CreateField', 'Transactions', 
     185          'DeleteFeature', and 'FastSetNextByIndex'. 
     186        """ 
     187        return bool(test_capability(self._ptr, capability)) 
  • django/branches/gis/django/contrib/gis/gdal/prototypes/ds.py

    r6914 r8034  
    3838get_next_feature = voidptr_output(lgdal.OGR_L_GetNextFeature, [c_void_p]) 
    3939reset_reading = void_output(lgdal.OGR_L_ResetReading, [c_void_p], errcheck=False) 
     40test_capability = int_output(lgdal.OGR_L_TestCapability, [c_void_p, c_char_p]) 
    4041 
    4142### Feature Definition Routines ### 
  • django/branches/gis/django/contrib/gis/tests/test_gdal_ds.py

    r6686 r8034  
    44 
    55# Path for SHP files 
    6 shp_path = os.path.dirname(__file__
    7 def get_shp(name): 
    8     return shp_path + os.sep + name + os.sep + name + '.shp' 
     6data_path = os.path.join(os.path.dirname(__file__), 'data'
     7def get_ds_file(name, ext): 
     8    return os.sep.join([data_path, name, name + '.%s' % ext]) 
    99 
    1010# Test SHP data source object 
    11 class TestSHP: 
    12     def __init__(self, shp, **kwargs): 
    13         self.ds = get_shp(shp) 
     11class TestDS: 
     12    def __init__(self, name, **kwargs): 
     13        ext = kwargs.pop('ext', 'shp') 
     14        self.ds = get_ds_file(name, ext) 
    1415        for key, value in kwargs.items(): 
    1516            setattr(self, key, value) 
    1617 
    1718# List of acceptable data sources. 
    18 ds_list = (TestSHP('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,}, 
    19                    extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS 
    20                    srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'), 
    21            TestSHP('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3, 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]]'), 
     19ds_list = (TestDS('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='ESRI Shapefile', 
     20                  fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,}, 
     21                  extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS 
     22                  srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]', 
     23                  field_values={'dbl' : [float(i) for i in range(1, 6)], 'int' : range(1, 6), 'str' : [str(i) for i in range(1, 6)]}, 
     24                  fids=range(5)), 
     25           TestDS('test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype=1, driver='VRT', 
     26                  fields={'POINT_X' : OFTString, 'POINT_Y' : OFTString, 'NUM' : OFTString}, # VRT uses CSV, which all types are OFTString. 
     27                  extent=(1.0, 2.0, 100.0, 523.5), # Min/Max from CSV 
     28                  field_values={'POINT_X' : ['1.0', '5.0', '100.0'], 'POINT_Y' : ['2.0', '23.0', '523.5'], 'NUM' : ['5', '17', '23']}, 
     29                  fids=range(1,4)), 
     30           TestDS('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3,  
     31                  driver='ESRI Shapefile', 
     32                  fields={'float' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,}, 
     33                  extent=(-1.01513,-0.558245,0.161876,0.839637), # Got extent from QGIS 
     34                  srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'), 
    2435           ) 
    2536 
    26 bad_ds = (TestSHP('foo'), 
     37bad_ds = (TestDS('foo'), 
    2738          ) 
    2839 
     
    4354 
    4455            # Making sure the driver name matches up 
    45             self.assertEqual('ESRI Shapefile', str(ds.driver)) 
     56            self.assertEqual(source.driver, str(ds.driver)) 
    4657 
    4758            # Making sure indexing works 
     
    5869            self.assertRaises(OGRException, DataSource, source.ds) 
    5970 
    60     def test03_layers(self): 
     71    def test03a_layers(self): 
    6172        "Testing Data Source Layers." 
    62  
     73        print "\nBEGIN - expecting out of range feature id error; safe to ignore.\n" 
    6374        for source in ds_list: 
    6475            ds = DataSource(source.ds) 
    6576 
    66             # Incrementing through each layer, this tests __iter__ 
     77            # Incrementing through each layer, this tests DataSource.__iter__ 
    6778            for layer in ds:                 
    6879                # Making sure we get the number of features we expect 
    6980                self.assertEqual(len(layer), source.nfeat) 
    70  
    71                 layer[0] #can index 
    72                 layer[:1] #can slice 
    7381 
    7482                # Making sure we get the number of fields we expect 
     
    8694                flds = layer.fields 
    8795                for f in flds: self.assertEqual(True, f in source.fields) 
     96                 
     97                # Negative FIDs are not allowed. 
     98                self.assertRaises(OGRIndexError, layer.__getitem__, -1) 
     99                self.assertRaises(OGRIndexError, layer.__getitem__, 50000) 
     100 
     101                if hasattr(source, 'field_values'): 
     102                    fld_names = source.field_values.keys() 
     103 
     104                    # Testing `Layer.get_fields` (which uses Layer.__iter__) 
     105                    for fld_name in fld_names: 
     106                        self.assertEqual(source.field_values[fld_name], layer.get_fields(fld_name)) 
     107 
     108                    # Testing `Layer.__getitem__`. 
     109                    for i, fid in enumerate(source.fids): 
     110                        feat = layer[fid] 
     111                        self.assertEqual(fid, feat.fid) 
     112                        # Maybe this should be in the test below, but we might as well test 
     113                        # the feature values here while in this loop. 
     114                        for fld_name in fld_names: 
     115                            self.assertEqual(source.field_values[fld_name][i], feat.get(fld_name)) 
     116        print "\nEND - expecting out of range feature id error; safe to ignore." 
     117                         
     118    def test03b_layer_slice(self): 
     119        "Test indexing and slicing on Layers." 
     120        # Using the first data-source because the same slice 
     121        # can be used for both the layer and the control values. 
     122        source = ds_list[0] 
     123        ds = DataSource(source.ds) 
     124 
     125        sl = slice(1, 3) 
     126        feats = ds[0][sl] 
     127 
     128        for fld_name in ds[0].fields: 
     129            test_vals = [feat.get(fld_name) for feat in feats] 
     130            control_vals = source.field_values[fld_name][sl] 
     131            self.assertEqual(control_vals, test_vals) 
    88132 
    89133    def test04_features(self): 
     
    96140                # Incrementing through each feature in the layer 
    97141                for feat in layer: 
    98                     # Making sure the number of fields is what's expected. 
     142                    # Making sure the number of fields, and the geometry type 
     143                    # are what's expected. 
    99144                    self.assertEqual(source.nfld, len(list(feat))) 
    100145                    self.assertEqual(source.gtype, feat.geom_type) 
     
    106151                        self.assertEqual(True, isinstance(feat[k], v)) 
    107152 
    108                     # Testing __iter__ on the Feature 
     153                    # Testing Feature.__iter__ 
    109154                    for fld in feat: self.assertEqual(True, fld.name in source.fields.keys()) 
    110155                         
     
    124169 
    125170                    # Making sure the SpatialReference is as expected. 
    126                     self.assertEqual(source.srs_wkt, g.srs.wkt) 
    127                          
     171                    if hasattr(source, 'srs_wkt'): 
     172                        self.assertEqual(source.srs_wkt, g.srs.wkt) 
     173 
     174 
    128175def suite(): 
    129176    s = unittest.TestSuite() 
  • django/branches/gis/django/contrib/gis/tests/test_gdal_geom.py

    r7665 r8034  
    3636        self.assertEqual(False, OGRGeomType(1) != OGRGeomType('point')) 
    3737        self.assertEqual(True, OGRGeomType('POINT') != OGRGeomType(6)) 
     38 
     39        # Testing the Django field name equivalent property. 
     40        self.assertEqual('PointField', OGRGeomType('Point').django) 
     41        self.assertEqual(None, OGRGeomType('Unknown').django) 
     42        self.assertEqual(None, OGRGeomType('none').django) 
    3843 
    3944    def test01a_wkt(self):