diff --git a/django/contrib/gis/gdal/layer.py b/django/contrib/gis/gdal/layer.py index cf5e57866e..a2163bc3c8 100644 --- a/django/contrib/gis/gdal/layer.py +++ b/django/contrib/gis/gdal/layer.py @@ -1,5 +1,5 @@ # Needed ctypes routines -from ctypes import byref +from ctypes import c_double, byref # Other GDAL imports. from django.contrib.gis.gdal.base import GDALBase @@ -7,11 +7,12 @@ from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException from django.contrib.gis.gdal.feature import Feature from django.contrib.gis.gdal.field import OGRFieldTypes -from django.contrib.gis.gdal.geometries import OGRGeomType +from django.contrib.gis.gdal.geomtype import OGRGeomType +from django.contrib.gis.gdal.geometries import OGRGeometry from django.contrib.gis.gdal.srs import SpatialReference # GDAL ctypes function prototypes. -from django.contrib.gis.gdal.prototypes import ds as capi, srs as srs_api +from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api, srs as srs_api # For more information, see the OGR C API source code: # http://www.gdal.org/ogr/ogr__api_8h.html @@ -156,6 +157,29 @@ class Layer(GDALBase): return [capi.get_field_precision(capi.get_field_defn(self._ldefn, i)) for i in xrange(self.num_fields)] + def _get_spatial_filter(self): + try: + return OGRGeometry(geom_api.clone_geom(capi.get_spatial_filter(self.ptr))) + except OGRException: + return None + + def _set_spatial_filter(self, filter): + if isinstance(filter, OGRGeometry): + capi.set_spatial_filter(self.ptr, filter.ptr) + elif isinstance(filter, (tuple, list)): + if not len(filter) == 4: + raise ValueError('Spatial filter list/tuple must have 4 elements.') + # Map c_double onto params -- if a bad type is passed in it + # will be caught here. + xmin, ymin, xmax, ymax = map(c_double, filter) + capi.set_spatial_filter_rect(self.ptr, xmin, ymin, xmax, ymax) + elif filter is None: + capi.set_spatial_filter(self.ptr, None) + else: + raise TypeError('Spatial filter must be either an OGRGeometry instance, a 4-tuple, or None.') + + spatial_filter = property(_get_spatial_filter, _set_spatial_filter) + #### Layer Methods #### def get_fields(self, field_name): """ diff --git a/django/contrib/gis/gdal/prototypes/ds.py b/django/contrib/gis/gdal/prototypes/ds.py index b64183eeb3..44828ee5f9 100644 --- a/django/contrib/gis/gdal/prototypes/ds.py +++ b/django/contrib/gis/gdal/prototypes/ds.py @@ -3,7 +3,7 @@ related data structures. OGR_Dr_*, OGR_DS_*, OGR_L_*, OGR_F_*, OGR_Fld_* routines are relevant here. """ -from ctypes import c_char_p, c_int, c_long, c_void_p, POINTER +from ctypes import c_char_p, c_double, c_int, c_long, c_void_p, POINTER from django.contrib.gis.gdal.envelope import OGREnvelope from django.contrib.gis.gdal.libgdal import lgdal from django.contrib.gis.gdal.prototypes.generation import \ @@ -38,6 +38,9 @@ get_layer_srs = srs_output(lgdal.OGR_L_GetSpatialRef, [c_void_p]) get_next_feature = voidptr_output(lgdal.OGR_L_GetNextFeature, [c_void_p]) reset_reading = void_output(lgdal.OGR_L_ResetReading, [c_void_p], errcheck=False) test_capability = int_output(lgdal.OGR_L_TestCapability, [c_void_p, c_char_p]) +get_spatial_filter = geom_output(lgdal.OGR_L_GetSpatialFilter, [c_void_p]) +set_spatial_filter = void_output(lgdal.OGR_L_SetSpatialFilter, [c_void_p, c_void_p], errcheck=False) +set_spatial_filter_rect = void_output(lgdal.OGR_L_SetSpatialFilterRect, [c_void_p, c_double, c_double, c_double, c_double], errcheck=False) ### Feature Definition Routines ### get_fd_geom_type = int_output(lgdal.OGR_FD_GetGeomType, [c_void_p]) diff --git a/django/contrib/gis/gdal/tests/test_ds.py b/django/contrib/gis/gdal/tests/test_ds.py index 30ce462475..a955f8cd8a 100644 --- a/django/contrib/gis/gdal/tests/test_ds.py +++ b/django/contrib/gis/gdal/tests/test_ds.py @@ -1,13 +1,11 @@ import os, os.path, unittest -from django.contrib.gis.gdal import DataSource, Envelope, OGRException, OGRIndexError +from django.contrib.gis.gdal import DataSource, Envelope, OGRGeometry, OGRException, OGRIndexError from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString from django.contrib import gis # Path for SHP files data_path = os.path.join(os.path.dirname(gis.__file__), 'tests' + os.sep + 'data') def get_ds_file(name, ext): - - return os.sep.join([data_path, name, name + '.%s' % ext]) # Test SHP data source object @@ -191,7 +189,41 @@ class DataSourceTest(unittest.TestCase): if hasattr(source, 'srs_wkt'): self.assertEqual(source.srs_wkt, g.srs.wkt) + def test06_spatial_filter(self): + "Testing the Layer.spatial_filter property." + ds = DataSource(get_ds_file('cities', 'shp')) + lyr = ds[0] + # When not set, it should be None. + self.assertEqual(None, lyr.spatial_filter) + + # Must be set a/an OGRGeometry or 4-tuple. + self.assertRaises(TypeError, lyr._set_spatial_filter, 'foo') + + # Setting the spatial filter with a tuple/list with the extent of + # a buffer centering around Pueblo. + self.assertRaises(ValueError, lyr._set_spatial_filter, range(5)) + filter_extent = (-105.609252, 37.255001, -103.609252, 39.255001) + lyr.spatial_filter = (-105.609252, 37.255001, -103.609252, 39.255001) + self.assertEqual(OGRGeometry.from_bbox(filter_extent), lyr.spatial_filter) + feats = [feat for feat in lyr] + self.assertEqual(1, len(feats)) + self.assertEqual('Pueblo', feats[0].get('Name')) + + # Setting the spatial filter with an OGRGeometry for buffer centering + # around Houston. + filter_geom = OGRGeometry('POLYGON((-96.363151 28.763374,-94.363151 28.763374,-94.363151 30.763374,-96.363151 30.763374,-96.363151 28.763374))') + lyr.spatial_filter = filter_geom + self.assertEqual(filter_geom, lyr.spatial_filter) + feats = [feat for feat in lyr] + self.assertEqual(1, len(feats)) + self.assertEqual('Houston', feats[0].get('Name')) + + # Clearing the spatial filter by setting it to None. Now + # should indicate that there are 3 features in the Layer. + lyr.spatial_filter = None + self.assertEqual(3, len(lyr)) + def suite(): s = unittest.TestSuite() s.addTest(unittest.makeSuite(DataSourceTest))