From 9041c95464a5d844051df9aa450de067f7cbdd43 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 6 May 2016 17:03:51 +1000 Subject: [PATCH] Allow injecting extra properties into feature's GeoJSON export Sponsored by Kanton of Zug, Switzerland --- python/core/qgsjsonutils.sip | 2 ++ src/core/qgsjsonutils.cpp | 46 +++++++++++++++++++-------- src/core/qgsjsonutils.h | 2 ++ src/server/qgswfsserver.cpp | 2 +- tests/src/python/test_qgsjsonutils.py | 38 ++++++++++++++++++++++ 5 files changed, 75 insertions(+), 15 deletions(-) diff --git a/python/core/qgsjsonutils.sip b/python/core/qgsjsonutils.sip index 1c3a986fa86f..b237a77f676f 100644 --- a/python/core/qgsjsonutils.sip +++ b/python/core/qgsjsonutils.sip @@ -66,11 +66,13 @@ class QgsJSONExporter /** Returns a GeoJSON string representation of a feature. * @param feature feature to convert + * @param extraProperties map of extra attributes to include in feature's properties * @param id optional ID to use as GeoJSON feature's ID instead of input feature's ID. If omitted, feature's * ID is used. * @returns GeoJSON string */ QString exportFeature( const QgsFeature& feature, + const QVariantMap& extraProperties = QVariantMap(), const QVariant& id = QVariant() ) const; }; diff --git a/src/core/qgsjsonutils.cpp b/src/core/qgsjsonutils.cpp index fce62757e89a..3532ca9e5718 100644 --- a/src/core/qgsjsonutils.cpp +++ b/src/core/qgsjsonutils.cpp @@ -26,14 +26,15 @@ QgsJSONExporter::QgsJSONExporter( int precision, bool includeGeometry, bool incl } -QString QgsJSONExporter::exportFeature( const QgsFeature& feature, const QVariant& id ) const +QString QgsJSONExporter::exportFeature( const QgsFeature& feature, const QVariantMap& extraProperties, + const QVariant& id ) const { QString s = "{\n \"type\":\"Feature\",\n"; // ID s += QString( " \"id\":%1" ).arg( !id.isValid() ? QString::number( feature.id() ) : QgsJSONUtils::encodeValue( id ) ); - if ( mIncludeAttributes || mIncludeGeometry ) + if ( mIncludeAttributes || mIncludeGeometry || !extraProperties.isEmpty() ) s += ",\n"; else s += '\n'; @@ -52,32 +53,49 @@ QString QgsJSONExporter::exportFeature( const QgsFeature& feature, const QVarian } s += " \"geometry\":\n "; s += geom->exportToGeoJSON( mPrecision ); - if ( mIncludeAttributes ) + if ( mIncludeAttributes || !extraProperties.isEmpty() ) s += ",\n"; else s += '\n'; } - if ( mIncludeAttributes ) + if ( mIncludeAttributes || !extraProperties.isEmpty() ) { //read all attribute values from the feature s += " \"properties\":{\n"; - - const QgsFields* fields = feature.fields(); int attributeCounter = 0; - for ( int i = 0; i < fields->count(); ++i ) + if ( mIncludeAttributes ) { - if ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) - continue; + const QgsFields* fields = feature.fields(); - if ( attributeCounter > 0 ) - s += ",\n"; - QVariant val = feature.attributes().at( i ); + for ( int i = 0; i < fields->count(); ++i ) + { + if ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) + continue; - s += QString( " \"%1\":%2" ).arg( fields->at( i ).name(), QgsJSONUtils::encodeValue( val ) ); + if ( attributeCounter > 0 ) + s += ",\n"; + QVariant val = feature.attributes().at( i ); - ++attributeCounter; + s += QString( " \"%1\":%2" ).arg( fields->at( i ).name(), QgsJSONUtils::encodeValue( val ) ); + + ++attributeCounter; + } + } + + if ( !extraProperties.isEmpty() ) + { + QVariantMap::const_iterator it = extraProperties.constBegin(); + for ( ; it != extraProperties.constEnd(); ++it ) + { + if ( attributeCounter > 0 ) + s += ",\n"; + + s += QString( " \"%1\":%2" ).arg( it.key(), QgsJSONUtils::encodeValue( it.value() ) ); + + ++attributeCounter; + } } s += "\n }\n"; diff --git a/src/core/qgsjsonutils.h b/src/core/qgsjsonutils.h index 5c31f93e4368..a66ec9003441 100644 --- a/src/core/qgsjsonutils.h +++ b/src/core/qgsjsonutils.h @@ -85,11 +85,13 @@ class CORE_EXPORT QgsJSONExporter /** Returns a GeoJSON string representation of a feature. * @param feature feature to convert + * @param extraProperties map of extra attributes to include in feature's properties * @param id optional ID to use as GeoJSON feature's ID instead of input feature's ID. If omitted, feature's * ID is used. * @returns GeoJSON string */ QString exportFeature( const QgsFeature& feature, + const QVariantMap& extraProperties = QVariantMap(), const QVariant& id = QVariant() ) const; private: diff --git a/src/server/qgswfsserver.cpp b/src/server/qgswfsserver.cpp index a99c7cbeebe2..cf3e4e2c2601 100644 --- a/src/server/qgswfsserver.cpp +++ b/src/server/qgswfsserver.cpp @@ -1913,7 +1913,7 @@ QString QgsWFSServer::createFeatureGeoJSON( QgsFeature* feat, int prec, QgsCoord exporter.setIncludeAttributes( !attrsToExport.isEmpty() ); exporter.setAttributes( attrsToExport ); - return exporter.exportFeature( f, id ); + return exporter.exportFeature( f, QVariantMap(), id ); } QDomElement QgsWFSServer::createFeatureGML2( QgsFeature* feat, QDomDocument& doc, int prec, QgsCoordinateReferenceSystem& crs, const QgsAttributeList& attrIndexes, const QSet& excludedAttributes ) /*const*/ diff --git a/tests/src/python/test_qgsjsonutils.py b/tests/src/python/test_qgsjsonutils.py index c0e3d0e2bc47..17d10037d535 100644 --- a/tests/src/python/test_qgsjsonutils.py +++ b/tests/src/python/test_qgsjsonutils.py @@ -248,5 +248,43 @@ def testJSONExporter(self): }""" self.assertEqual(exporter.exportFeature(feature, id=29), expected) + # test injecting extra attributes + expected = """{ + "type":"Feature", + "id":5, + "properties":{ + "name":"Valsier Peninsula", + "cost":6.8, + "population":198, + "extra":"val1", + "extra2":2 + } +}""" + self.assertEqual(exporter.exportFeature(feature, extraProperties={"extra": "val1", "extra2": 2}), expected) + + exporter.setIncludeAttributes(False) + expected = """{ + "type":"Feature", + "id":5, + "properties":{ + "extra":"val1", + "extra2":{"nested_map":5, +"nested_map2":"val"}, + "extra3":[1,2,3] + } +}""" + self.assertEqual(exporter.exportFeature(feature, extraProperties={"extra": "val1", "extra2": {"nested_map": 5, "nested_map2": "val"}, "extra3": [1, 2, 3]}), expected) + exporter.setIncludeGeometry(True) + expected = """{ + "type":"Feature", + "id":5, + "geometry": + {"type": "Point", "coordinates": [5, 6]}, + "properties":{ + "extra":"val1", + "extra2":2 + } +}""" + if __name__ == "__main__": unittest.main()