Skip to content
Permalink
Browse files

Allow setting list of attributes to exclude from json exports

  • Loading branch information
nyalldawson committed May 6, 2016
1 parent 9041c95 commit 34d468e64bf1361f360a442381deb93b455b4f85
Showing with 149 additions and 56 deletions.
  1. +21 −0 python/core/qgsjsonutils.sip
  2. +46 −38 src/core/qgsjsonutils.cpp
  3. +25 −18 src/core/qgsjsonutils.h
  4. +57 −0 tests/src/python/test_qgsjsonutils.py
@@ -55,15 +55,36 @@ class QgsJSONExporter
* @param attributes list of attribute indexes, or an empty list to include all
* attributes
* @see attributes()
* @see setExcludedAttributes()
* @note Attributes excluded via setExcludedAttributes() take precedence over
* attributes specified by this method.
*/
void setAttributes( const QgsAttributeList& attributes );

/** Returns the list of attributes which will be included in the JSON exports, or
* an empty list if all attributes will be included.
* @see setAttributes()
* @see excludedAttributes()
* @note Attributes excluded via excludedAttributes() take precedence over
* attributes returned by this method.
*/
QgsAttributeList attributes() const;

/** Sets a list of attributes to specifically exclude from the JSON exports. Excluded attributes
* take precedence over attributes included via setAttributes().
* @param attributes list of attribute indexes to exclude
* @see excludedAttributes()
* @see setAttributes()
*/
void setExcludedAttributes( const QgsAttributeList& attributes );

/** Returns a list of attributes which will be specifically excluded from the JSON exports. Excluded attributes
* take precedence over attributes included via attributes().
* @see setExcludedAttributes()
* @see attributes()
*/
QgsAttributeList excludedAttributes() const;

/** 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
@@ -29,56 +29,29 @@ QgsJSONExporter::QgsJSONExporter( int precision, bool includeGeometry, bool incl
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 || !extraProperties.isEmpty() )
s += ",\n";
else
s += '\n';

const QgsGeometry* geom = feature.constGeometry();
if ( geom && !geom->isEmpty() && mIncludeGeometry )
{
QgsRectangle box = geom->boundingBox();

if ( QgsWKBTypes::flatType( geom->geometry()->wkbType() ) != QgsWKBTypes::Point )
{
s += QString( " \"bbox\":[%1, %2, %3, %4],\n" ).arg( qgsDoubleToString( box.xMinimum(), mPrecision ),
qgsDoubleToString( box.yMinimum(), mPrecision ),
qgsDoubleToString( box.xMaximum(), mPrecision ),
qgsDoubleToString( box.yMaximum(), mPrecision ) );
}
s += " \"geometry\":\n ";
s += geom->exportToGeoJSON( mPrecision );
if ( mIncludeAttributes || !extraProperties.isEmpty() )
s += ",\n";
else
s += '\n';
}
//first step is to build up the properties list
QString properties;

int attributeCounter = 0;
if ( mIncludeAttributes || !extraProperties.isEmpty() )
{
//read all attribute values from the feature
s += " \"properties\":{\n";
int attributeCounter = 0;


if ( mIncludeAttributes )
{
const QgsFields* fields = feature.fields();

for ( int i = 0; i < fields->count(); ++i )
{
if ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) )
if (( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) || mExcludedAttributeIndexes.contains( i ) )
continue;

if ( attributeCounter > 0 )
s += ",\n";
properties += ",\n";
QVariant val = feature.attributes().at( i );

s += QString( " \"%1\":%2" ).arg( fields->at( i ).name(), QgsJSONUtils::encodeValue( val ) );
properties += QString( " \"%1\":%2" ).arg( fields->at( i ).name(), QgsJSONUtils::encodeValue( val ) );

++attributeCounter;
}
@@ -90,18 +63,53 @@ QString QgsJSONExporter::exportFeature( const QgsFeature& feature, const QVarian
for ( ; it != extraProperties.constEnd(); ++it )
{
if ( attributeCounter > 0 )
s += ",\n";
properties += ",\n";

s += QString( " \"%1\":%2" ).arg( it.key(), QgsJSONUtils::encodeValue( it.value() ) );
properties += QString( " \"%1\":%2" ).arg( it.key(), QgsJSONUtils::encodeValue( it.value() ) );

++attributeCounter;
}
}
}
bool hasProperties = attributeCounter > 0;

s += "\n }\n";
QString s = "{\n \"type\":\"Feature\",\n";

// ID
s += QString( " \"id\":%1" ).arg( !id.isValid() ? QString::number( feature.id() ) : QgsJSONUtils::encodeValue( id ) );

if ( hasProperties || mIncludeGeometry )
s += ",\n";
else
s += '\n';

const QgsGeometry* geom = feature.constGeometry();
if ( geom && !geom->isEmpty() && mIncludeGeometry )
{
QgsRectangle box = geom->boundingBox();

if ( QgsWKBTypes::flatType( geom->geometry()->wkbType() ) != QgsWKBTypes::Point )
{
s += QString( " \"bbox\":[%1, %2, %3, %4],\n" ).arg( qgsDoubleToString( box.xMinimum(), mPrecision ),
qgsDoubleToString( box.yMinimum(), mPrecision ),
qgsDoubleToString( box.xMaximum(), mPrecision ),
qgsDoubleToString( box.yMaximum(), mPrecision ) );
}
s += " \"geometry\":\n ";
s += geom->exportToGeoJSON( mPrecision );
if ( hasProperties )
s += ",\n";
else
s += '\n';
}

if ( hasProperties )
{
//read all attribute values from the feature
s += " \"properties\":{\n" + properties + "\n }\n";
}

s += "}";
s += '}';

return s;
}
@@ -74,15 +74,36 @@ class CORE_EXPORT QgsJSONExporter
* @param attributes list of attribute indexes, or an empty list to include all
* attributes
* @see attributes()
* @see setExcludedAttributes()
* @note Attributes excluded via setExcludedAttributes() take precedence over
* attributes specified by this method.
*/
void setAttributes( const QgsAttributeList& attributes ) { mAttributeIndexes = attributes; }

/** Returns the list of attributes which will be included in the JSON exports, or
* an empty list if all attributes will be included.
* @see setAttributes()
* @see excludedAttributes()
* @note Attributes excluded via excludedAttributes() take precedence over
* attributes returned by this method.
*/
QgsAttributeList attributes() const { return mAttributeIndexes; }

/** Sets a list of attributes to specifically exclude from the JSON exports. Excluded attributes
* take precedence over attributes included via setAttributes().
* @param attributes list of attribute indexes to exclude
* @see excludedAttributes()
* @see setAttributes()
*/
void setExcludedAttributes( const QgsAttributeList& attributes ) { mExcludedAttributeIndexes = attributes; }

/** Returns a list of attributes which will be specifically excluded from the JSON exports. Excluded attributes
* take precedence over attributes included via attributes().
* @see setExcludedAttributes()
* @see attributes()
*/
QgsAttributeList excludedAttributes() const { return mExcludedAttributeIndexes; }

/** 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
@@ -100,8 +121,12 @@ class CORE_EXPORT QgsJSONExporter
int mPrecision;

//! List of attribute indexes to include in export, or empty list to include all attributes
//! @see mExcludedAttributeIndexes
QgsAttributeList mAttributeIndexes;

//! List of attribute indexes to exclude from export
QgsAttributeList mExcludedAttributeIndexes;

//! Whether to include geometry in JSON export
bool mIncludeGeometry;

@@ -139,24 +164,6 @@ class CORE_EXPORT QgsJSONUtils
*/
static QgsFields stringToFields( const QString& string, QTextCodec* encoding );

/** Returns a GeoJSON string representation of a feature.
* @param feature feature to convert
* @param precision maximum number of decimal places to use for geometry coordinates
* @param attrIndexes list of attribute indexes to include in GeoJSON, or an empty list to include
* all attributes
* @param includeGeom set to false to avoid including the geometry representation in the JSON output
* @param includeAttributes set to false to avoid including any attribute values in the JSON output
* @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
*/
static QString featureToGeoJSON( const QgsFeature& feature,
int precision = 17,
const QgsAttributeList& attrIndexes = QgsAttributeList(),
bool includeGeom = true,
bool includeAttributes = true,
const QVariant& id = QVariant() );

/** Encodes a value to a JSON string representation, adding appropriate quotations and escaping
* where required.
* @param value value to encode
@@ -198,6 +198,63 @@ def testJSONExporter(self):
self.assertEqual(exporter.exportFeature(feature), expected)
exporter.setAttributes([])

# text excluding attributes

exporter.setExcludedAttributes([1])
self.assertEqual(exporter.excludedAttributes(), [1])
expected = """{
"type":"Feature",
"id":5,
"geometry":
{"type": "Point", "coordinates": [5, 6]},
"properties":{
"name":"Valsier Peninsula",
"population":198
}
}"""
self.assertEqual(exporter.exportFeature(feature), expected)

exporter.setExcludedAttributes([1, 2])
self.assertEqual(exporter.excludedAttributes(), [1, 2])
expected = """{
"type":"Feature",
"id":5,
"geometry":
{"type": "Point", "coordinates": [5, 6]},
"properties":{
"name":"Valsier Peninsula"
}
}"""
self.assertEqual(exporter.exportFeature(feature), expected)

exporter.setExcludedAttributes([0, 1, 2])
self.assertEqual(exporter.excludedAttributes(), [0, 1, 2])
expected = """{
"type":"Feature",
"id":5,
"geometry":
{"type": "Point", "coordinates": [5, 6]}
}"""
self.assertEqual(exporter.exportFeature(feature), expected)

# test that excluded attributes take precedence over included

exporter.setAttributes([1, 2])
exporter.setExcludedAttributes([0, 1])
expected = """{
"type":"Feature",
"id":5,
"geometry":
{"type": "Point", "coordinates": [5, 6]},
"properties":{
"population":198
}
}"""
self.assertEqual(exporter.exportFeature(feature), expected)

exporter.setAttributes([])
exporter.setExcludedAttributes([])

# test excluding geometry
exporter.setIncludeGeometry(False)
self.assertEqual(exporter.includeGeometry(), False)

0 comments on commit 34d468e

Please sign in to comment.
You can’t perform that action at this time.