From 356fe1280c7d28bac4ddf2495e83e59d52f2ed27 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Tue, 19 Apr 2022 13:44:56 -0400 Subject: [PATCH] add skip_geometry to remaining feature providers (#887) * skip geometry for SQLite/GPKG backends (#886) * simplify GKPG * add for remaining providers, add to doc --- .../source/data-publishing/ogcapi-features.rst | 18 +++++++++--------- pygeoapi/provider/mongo.py | 12 +++++++----- pygeoapi/provider/ogr.py | 12 ++++++++---- pygeoapi/provider/sqlite.py | 8 ++++++-- tests/test_mongo_provider.py | 4 ++++ tests/test_ogr_csv_provider.py | 9 +++++++++ tests/test_sqlite_geopackage_provider.py | 9 +++++++++ 7 files changed, 52 insertions(+), 20 deletions(-) diff --git a/docs/source/data-publishing/ogcapi-features.rst b/docs/source/data-publishing/ogcapi-features.rst index 6593f89..3f23f12 100644 --- a/docs/source/data-publishing/ogcapi-features.rst +++ b/docs/source/data-publishing/ogcapi-features.rst @@ -15,17 +15,17 @@ pygeoapi core feature providers are listed below, along with a matrix of support parameters. .. csv-table:: - :header: Provider, property filters/display, resulttype, bbox, datetime, sortby, CQL + :header: Provider, property filters/display, resulttype, bbox, datetime, sortby, skipGeometry, CQL :align: left - CSV,✅/✅,results/hits,❌,❌,❌,❌ - Elasticsearch,✅/✅,results/hits,✅,✅,✅,✅ - GeoJSON,✅/✅,results/hits,❌,❌,❌,❌ - MongoDB,✅/❌,results,✅,✅,✅,❌ - OGR,✅/❌,results/hits,✅,❌,❌,❌ - PostgreSQL,✅/✅,results/hits,✅,❌,✅,❌ - SQLiteGPKG,✅/❌,results/hits,✅,❌,❌,❌ - SensorThingsAPI,✅/✅,results/hits,✅,✅,✅,❌ + CSV,✅/✅,results/hits,❌,❌,❌,✅,❌ + Elasticsearch,✅/✅,results/hits,✅,✅,✅,✅,✅ + GeoJSON,✅/✅,results/hits,❌,❌,❌,✅,❌ + MongoDB,✅/❌,results,✅,✅,✅,✅,❌ + OGR,✅/❌,results/hits,✅,❌,❌,✅,❌ + PostgreSQL,✅/✅,results/hits,✅,❌,✅,✅,❌ + SQLiteGPKG,✅/❌,results/hits,✅,❌,❌,✅,❌ + SensorThingsAPI,✅/✅,results/hits,✅,✅,✅,✅,❌ Below are specific connection examples based on supported providers. diff --git a/pygeoapi/provider/mongo.py b/pygeoapi/provider/mongo.py index 1fb55cc..74e3dc0 100644 --- a/pygeoapi/provider/mongo.py +++ b/pygeoapi/provider/mongo.py @@ -81,7 +81,8 @@ class MongoProvider(BaseProvider): map, reduce, "myresults") return result.distinct('_id') - def _get_feature_list(self, filterObj, sortList=[], skip=0, maxitems=1): + def _get_feature_list(self, filterObj, sortList=[], skip=0, maxitems=1, + skip_geometry=False): featurecursor = self.featuredb[self.collection].find(filterObj) if sortList: @@ -93,6 +94,8 @@ class MongoProvider(BaseProvider): featurelist = list(featurecursor) for item in featurelist: item['id'] = str(item.pop('_id')) + if skip_geometry: + item['geometry'] = None return featurelist, matchCount @@ -126,10 +129,9 @@ class MongoProvider(BaseProvider): ASCENDING if (sort['order'] == '+') else DESCENDING) for sort in sortby] - featurelist, matchcount = self._get_feature_list(filterobj, - sortList=sort_list, - skip=offset, - maxitems=limit) + featurelist, matchcount = self._get_feature_list( + filterobj, sortList=sort_list, skip=offset, maxitems=limit, + skip_geometry=skip_geometry) if resulttype == 'hits': featurelist = [] diff --git a/pygeoapi/provider/ogr.py b/pygeoapi/provider/ogr.py index 6a635c8..58069e7 100644 --- a/pygeoapi/provider/ogr.py +++ b/pygeoapi/provider/ogr.py @@ -353,7 +353,8 @@ class OGRProvider(BaseProvider): result = self._response_feature_hits(layer) elif resulttype == 'results': LOGGER.debug('results specified') - result = self._response_feature_collection(layer, limit) + result = self._response_feature_collection( + layer, limit, skip_geometry=skip_geometry) else: LOGGER.error('Invalid resulttype: %s' % resulttype) @@ -455,13 +456,15 @@ class OGRProvider(BaseProvider): LOGGER.error(self.gdal.GetLastErrorMsg()) raise gdalerr - def _ogr_feature_to_json(self, ogr_feature): + def _ogr_feature_to_json(self, ogr_feature, skip_geometry=False): geom = ogr_feature.GetGeometryRef() if self.transform_out: # Optionally reproject the geometry geom.Transform(self.transform_out) json_feature = ogr_feature.ExportToJson(as_object=True) + if skip_geometry: + json_feature['geometry'] = None try: json_feature['id'] = json_feature['properties'].pop(self.id_field) except Exception as err: @@ -470,7 +473,7 @@ class OGRProvider(BaseProvider): return json_feature - def _response_feature_collection(self, layer, limit): + def _response_feature_collection(self, layer, limit, skip_geometry=False): """ Assembles output from Layer query as GeoJSON FeatureCollection structure. @@ -492,7 +495,8 @@ class OGRProvider(BaseProvider): ogr_feature = _ignore_gdal_error(layer, 'GetNextFeature') count = 0 while ogr_feature is not None: - json_feature = self._ogr_feature_to_json(ogr_feature) + json_feature = self._ogr_feature_to_json( + ogr_feature, skip_geometry=skip_geometry) feature_collection['features'].append(json_feature) diff --git a/pygeoapi/provider/sqlite.py b/pygeoapi/provider/sqlite.py index 53698b3..cec1f56 100644 --- a/pygeoapi/provider/sqlite.py +++ b/pygeoapi/provider/sqlite.py @@ -128,11 +128,12 @@ class SQLiteGPKGProvider(BaseProvider): # WHERE continent=? : ('Europe',) return where_clause, where_values - def __response_feature(self, row_data): + def __response_feature(self, row_data, skip_geometry=False): """ Assembles GeoJSON output from DB query :param row_data: DB row result + :param skip_geometry: whether to skip geometry (default False) :returns: `dict` of GeoJSON Feature """ @@ -145,6 +146,9 @@ class SQLiteGPKGProvider(BaseProvider): feature["geometry"] = json.loads( rd.pop('AsGeoJSON({})'.format(self.geom_col)) ) + if skip_geometry: + feature["geometry"] = None + feature['properties'] = rd feature['id'] = feature['properties'].pop(self.id_field) @@ -306,7 +310,7 @@ class SQLiteGPKGProvider(BaseProvider): for rd in row_data: feature_collection['features'].append( - self.__response_feature(rd)) + self.__response_feature(rd, skip_geometry=skip_geometry)) return feature_collection diff --git a/tests/test_mongo_provider.py b/tests/test_mongo_provider.py index c91e8b2..279f672 100644 --- a/tests/test_mongo_provider.py +++ b/tests/test_mongo_provider.py @@ -96,6 +96,10 @@ def test_query(config): assert len(results['features'][0]['properties']) == 37 + results = p.query(skip_geometry=True) + for feature in results['features']: + assert feature['geometry'] is None + def test_get(config): p = MongoProvider(config) diff --git a/tests/test_ogr_csv_provider.py b/tests/test_ogr_csv_provider.py index 914beb7..7e14750 100644 --- a/tests/test_ogr_csv_provider.py +++ b/tests/test_ogr_csv_provider.py @@ -174,3 +174,12 @@ def test_query_with_property_vsicurl(config_vsicurl_csv): assert len(features) == 10 for feature in features: assert 'Lazio' in feature['properties']['denominazione_regione'] + + +def test_query_with_skip_geometry_vsicurl(config_vsicurl_csv): + """Testing query for a valid JSON object with property filter""" + + p = OGRProvider(config_vsicurl_csv) + feature_collection = p.query(skip_geometry=True) + for feature in feature_collection['features']: + assert feature['geometry'] is None diff --git a/tests/test_sqlite_geopackage_provider.py b/tests/test_sqlite_geopackage_provider.py index a65c448..02066ae 100644 --- a/tests/test_sqlite_geopackage_provider.py +++ b/tests/test_sqlite_geopackage_provider.py @@ -167,3 +167,12 @@ def test_get_geopackage_not_existing_item_raise_exception(config_geopackage): p = SQLiteGPKGProvider(config_geopackage) with pytest.raises(ProviderItemNotFoundError): p.get(-1) + + +def test_get_geopackage_skip_geometry(config_geopackage): + """Testing query for skipped geometry""" + p = SQLiteGPKGProvider(config_geopackage) + + feature_collection = p.query(skip_geometry=True) + for feature in feature_collection['features']: + assert feature['geomtry'] is None