From cb9f71dd99f0e524c871830b248cf28fcacd5753 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sun, 7 Oct 2012 16:20:29 +0200 Subject: [PATCH] Fixed #18640 -- Allowed access to GDAL Feature without Datasource Thanks Justin Bronn for improving my initial patch. --- django/contrib/gis/gdal/feature.py | 24 +++++++++++++++--------- django/contrib/gis/gdal/field.py | 20 ++++++++++++-------- django/contrib/gis/gdal/layer.py | 4 ++-- django/contrib/gis/gdal/tests/test_ds.py | 8 +++++++- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/django/contrib/gis/gdal/feature.py b/django/contrib/gis/gdal/feature.py index b8737cd1a0..cf154d74b8 100644 --- a/django/contrib/gis/gdal/feature.py +++ b/django/contrib/gis/gdal/feature.py @@ -16,15 +16,21 @@ from django.utils.six.moves import xrange # # The OGR_F_* routines are relevant here. class Feature(GDALBase): - "A class that wraps an OGR Feature, needs to be instantiated from a Layer object." + """ + This class that wraps an OGR Feature, needs to be instantiated + from a Layer object. + """ #### Python 'magic' routines #### - def __init__(self, feat, fdefn): - "Initializes on the pointers for the feature and the layer definition." - if not feat or not fdefn: + def __init__(self, feat, layer): + """ + Initializes on the feature pointers for the feature and the layer + definition, as well as the Layer. + """ + if not feat: raise OGRException('Cannot create OGR Feature, invalid pointer given.') self.ptr = feat - self._fdefn = fdefn + self._layer = layer def __del__(self): "Releases a reference to this object." @@ -43,7 +49,7 @@ class Feature(GDALBase): if index < 0 or index > self.num_fields: raise OGRIndexError('index out of range') i = index - return Field(self.ptr, i) + return Field(self, i) def __iter__(self): "Iterates over each field in the Feature." @@ -71,7 +77,7 @@ class Feature(GDALBase): @property def layer_name(self): "Returns the name of the layer for the feature." - return capi.get_feat_name(self._fdefn) + return capi.get_feat_name(self._layer._ldefn) @property def num_fields(self): @@ -81,7 +87,7 @@ class Feature(GDALBase): @property def fields(self): "Returns a list of fields in the Feature." - return [capi.get_field_name(capi.get_field_defn(self._fdefn, i)) + return [capi.get_field_name(capi.get_field_defn(self._layer._ldefn, i)) for i in xrange(self.num_fields)] @property @@ -94,7 +100,7 @@ class Feature(GDALBase): @property def geom_type(self): "Returns the OGR Geometry Type for this Feture." - return OGRGeomType(capi.get_fd_geom_type(self._fdefn)) + return OGRGeomType(capi.get_fd_geom_type(self._layer._ldefn)) #### Feature Methods #### def get(self, field): diff --git a/django/contrib/gis/gdal/field.py b/django/contrib/gis/gdal/field.py index 12dc8b921f..16230afdc8 100644 --- a/django/contrib/gis/gdal/field.py +++ b/django/contrib/gis/gdal/field.py @@ -9,12 +9,15 @@ from django.contrib.gis.gdal.prototypes import ds as capi # # The OGR_Fld_* routines are relevant here. class Field(GDALBase): - "A class that wraps an OGR Field, needs to be instantiated from a Feature object." + """ + This class wraps an OGR Field, and needs to be instantiated + from a Feature object. + """ #### Python 'magic' routines #### def __init__(self, feat, index): """ - Initializes on the feature pointer and the integer index of + Initializes on the feature object and the integer index of the field within the feature. """ # Setting the feature pointer and index. @@ -22,7 +25,7 @@ class Field(GDALBase): self._index = index # Getting the pointer for this field. - fld_ptr = capi.get_feat_field_defn(feat, index) + fld_ptr = capi.get_feat_field_defn(feat.ptr, index) if not fld_ptr: raise OGRException('Cannot create OGR Field, invalid pointer given.') self.ptr = fld_ptr @@ -42,21 +45,22 @@ class Field(GDALBase): #### Field Methods #### def as_double(self): "Retrieves the Field's value as a double (float)." - return capi.get_field_as_double(self._feat, self._index) + return capi.get_field_as_double(self._feat.ptr, self._index) def as_int(self): "Retrieves the Field's value as an integer." - return capi.get_field_as_integer(self._feat, self._index) + return capi.get_field_as_integer(self._feat.ptr, self._index) def as_string(self): "Retrieves the Field's value as a string." - return capi.get_field_as_string(self._feat, self._index) + return capi.get_field_as_string(self._feat.ptr, self._index) def as_datetime(self): "Retrieves the Field's value as a tuple of date & time components." yy, mm, dd, hh, mn, ss, tz = [c_int() for i in range(7)] - status = capi.get_field_as_datetime(self._feat, self._index, byref(yy), byref(mm), byref(dd), - byref(hh), byref(mn), byref(ss), byref(tz)) + status = capi.get_field_as_datetime( + self._feat.ptr, self._index, byref(yy), byref(mm), byref(dd), + byref(hh), byref(mn), byref(ss), byref(tz)) if status: return (yy, mm, dd, hh, mn, ss, tz) else: diff --git a/django/contrib/gis/gdal/layer.py b/django/contrib/gis/gdal/layer.py index d7bf6969ca..da0f566969 100644 --- a/django/contrib/gis/gdal/layer.py +++ b/django/contrib/gis/gdal/layer.py @@ -61,7 +61,7 @@ class Layer(GDALBase): # ResetReading() must be called before iteration is to begin. capi.reset_reading(self._ptr) for i in xrange(self.num_feat): - yield Feature(capi.get_next_feature(self._ptr), self._ldefn) + yield Feature(capi.get_next_feature(self._ptr), self) def __len__(self): "The length is the number of features." @@ -81,7 +81,7 @@ class Layer(GDALBase): if self._random_read: # If the Layer supports random reading, return. try: - return Feature(capi.get_feature(self.ptr, feat_id), self._ldefn) + return Feature(capi.get_feature(self.ptr, feat_id), self) except OGRException: pass else: diff --git a/django/contrib/gis/gdal/tests/test_ds.py b/django/contrib/gis/gdal/tests/test_ds.py index 69e3054422..9ac1bc8d70 100644 --- a/django/contrib/gis/gdal/tests/test_ds.py +++ b/django/contrib/gis/gdal/tests/test_ds.py @@ -125,7 +125,10 @@ class DataSourceTest(unittest.TestCase): self.assertEqual(control_vals, test_vals) def test03c_layer_references(self): - "Test to make sure Layer access is still available without the DataSource." + """ + Test to make sure Layer/Feature access is still available without + the DataSource/Feature. + """ source = ds_list[0] # See ticket #9448. @@ -141,6 +144,9 @@ class DataSourceTest(unittest.TestCase): self.assertEqual(source.nfeat, len(lyr)) self.assertEqual(source.gtype, lyr.geom_type.num) + # Same issue for Feature/Field objects, see #18640 + self.assertEqual(str(lyr[0]['str']), "1") + def test04_features(self): "Testing Data Source Features." for source in ds_list: