Skip to content
Permalink
Browse files
Merge pull request #4627 from nyalldawson/json_format
Use field formatter when exporting feature attributes to JSON
  • Loading branch information
nyalldawson committed May 26, 2017
2 parents 9cfe70c + 5b0bc93 commit 86ce4418b1cccad563e8cc34eec80ea64d02fb2c
@@ -1,5 +1,5 @@
# black list
PyQgsJSONUtils
PyQgsJsonUtils
PyQgsLocalServer
PyQgsPalLabelingServer
qgis_composermapgridtest
@@ -104,6 +104,8 @@ Renamed Classes {#qgis_api_break_3_0_renamed_classes}
<tr><td>QgsGraduatedSymbolRendererV2Model<td>QgsGraduatedSymbolRendererModel
<tr><td>QgsGraduatedSymbolRendererV2ViewStyle<td>QgsGraduatedSymbolRendererViewStyle
<tr><td>QgsGraduatedSymbolRendererV2Widget<td>QgsGraduatedSymbolRendererWidget
<tr><td>QgsJSONExporter<td>QgsJsonExporter
<tr><td>QgsJSONUtils<td>QgsJsonUtils
<tr><td>QgsLabelingEngineV2<td>QgsLabelingEngine
<tr><td>QgsLegendModelV2<td>QgsLegendModel
<tr><td>QgsLegendSymbolItemV2<td>QgsLegendSymbolItem
@@ -10,7 +10,7 @@



class QgsJSONExporter
class QgsJsonExporter
{
%Docstring
Handles exporting QgsFeature features to GeoJSON features.
@@ -25,9 +25,9 @@ class QgsJSONExporter
%End
public:

QgsJSONExporter( const QgsVectorLayer *vectorLayer = 0, int precision = 6 );
QgsJsonExporter( QgsVectorLayer *vectorLayer = 0, int precision = 6 );
%Docstring
Constructor for QgsJSONExporter.
Constructor for QgsJsonExporter.
\param vectorLayer associated vector layer (required for related attribute export)
\param precision maximum number of decimal places to use for geometry coordinates,
the RFC 7946 GeoJSON specification recommends limiting coordinate precision to 6
@@ -94,7 +94,7 @@ class QgsJSONExporter
:rtype: bool
%End

void setVectorLayer( const QgsVectorLayer *vectorLayer );
void setVectorLayer( QgsVectorLayer *vectorLayer );
%Docstring
Sets the associated vector layer (required for related attribute export). This will automatically
update the sourceCrs() to match.
@@ -199,7 +199,7 @@ class QgsJSONExporter
};


class QgsJSONUtils
class QgsJsonUtils
{
%Docstring
Helper utilities for working with JSON and GeoJSON conversions.
@@ -247,10 +247,15 @@ class QgsJSONUtils
:rtype: str
%End

static QString exportAttributes( const QgsFeature &feature );
static QString exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer = 0,
QVector<QVariant> attributeWidgetCaches = QVector<QVariant>() );
%Docstring
Exports all attributes from a QgsFeature as a JSON map type.
\param feature feature to export
\param layer optional associated vector layer. If specified, this allows
richer export utilising settings like the layer's fields widget configuration.
\param attributeWidgetCaches optional widget configuration cache. Can be used
to speed up exporting the attributes for multiple features from the same layer.
:rtype: str
%End

@@ -142,7 +142,7 @@ QString QgsClipboard::generateClipboardText() const
}
case GeoJSON:
{
QgsJSONExporter exporter;
QgsJsonExporter exporter;
exporter.setSourceCrs( mCRS );
return exporter.exportFeatures( mFeatureClipboard );
}
@@ -551,7 +551,7 @@ void QgsComposerHtml::setExpressionContext( const QgsFeature &feature, QgsVector
}

// create JSON representation of feature
QgsJSONExporter exporter( layer );
QgsJsonExporter exporter( layer );
exporter.setIncludeRelated( true );
mAtlasFeatureJSON = exporter.exportFeature( feature );
}
@@ -23,16 +23,18 @@
#include "qgsproject.h"
#include "qgscsexception.h"
#include "qgslogger.h"
#include "qgsfieldformatterregistry.h"
#include "qgsfieldformatter.h"

#include <QJsonDocument>
#include <QJsonArray>

QgsJSONExporter::QgsJSONExporter( const QgsVectorLayer *vectorLayer, int precision )
QgsJsonExporter::QgsJsonExporter( QgsVectorLayer *vectorLayer, int precision )
: mPrecision( precision )
, mIncludeGeometry( true )
, mIncludeAttributes( true )
, mIncludeRelatedAttributes( false )
, mLayerId( vectorLayer ? vectorLayer->id() : QString() )
, mLayer( vectorLayer )
{
if ( vectorLayer )
{
@@ -42,39 +44,39 @@ QgsJSONExporter::QgsJSONExporter( const QgsVectorLayer *vectorLayer, int precisi
mTransform.setDestinationCrs( QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ) );
}

void QgsJSONExporter::setVectorLayer( const QgsVectorLayer *vectorLayer )
void QgsJsonExporter::setVectorLayer( QgsVectorLayer *vectorLayer )
{
mLayerId = vectorLayer ? vectorLayer->id() : QString();
mLayer = vectorLayer;
if ( vectorLayer )
{
mCrs = vectorLayer->crs();
mTransform.setSourceCrs( mCrs );
}
}

QgsVectorLayer *QgsJSONExporter::vectorLayer() const
QgsVectorLayer *QgsJsonExporter::vectorLayer() const
{
return qobject_cast< QgsVectorLayer * >( QgsProject::instance()->mapLayer( mLayerId ) );
return mLayer.data();
}

void QgsJSONExporter::setSourceCrs( const QgsCoordinateReferenceSystem &crs )
void QgsJsonExporter::setSourceCrs( const QgsCoordinateReferenceSystem &crs )
{
mCrs = crs;
mTransform.setSourceCrs( mCrs );
}

QgsCoordinateReferenceSystem QgsJSONExporter::sourceCrs() const
QgsCoordinateReferenceSystem QgsJsonExporter::sourceCrs() const
{
return mCrs;
}

QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties,
QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties,
const QVariant &id ) const
{
QString s = QStringLiteral( "{\n \"type\":\"Feature\",\n" );

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

QgsGeometry geom = feature.geometry();
if ( !geom.isNull() && mIncludeGeometry )
@@ -119,7 +121,7 @@ QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVarian

if ( mIncludeAttributes )
{
QgsFields fields = feature.fields();
QgsFields fields = mLayer ? mLayer->fields() : feature.fields();

for ( int i = 0; i < fields.count(); ++i )
{
@@ -130,7 +132,15 @@ QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVarian
properties += QLatin1String( ",\n" );
QVariant val = feature.attributes().at( i );

properties += QStringLiteral( " \"%1\":%2" ).arg( fields.at( i ).name(), QgsJSONUtils::encodeValue( val ) );
if ( mLayer )
{
QgsEditorWidgetSetup setup = fields.at( i ).editorWidgetSetup();
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
val = fieldFormatter->representValue( mLayer.data(), i, setup.config(), QVariant(), val );
}

properties += QStringLiteral( " \"%1\":%2" ).arg( fields.at( i ).name(), QgsJsonUtils::encodeValue( val ) );

++attributeCounter;
}
@@ -144,17 +154,16 @@ QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVarian
if ( attributeCounter > 0 )
properties += QLatin1String( ",\n" );

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

++attributeCounter;
}
}

// related attributes
QgsVectorLayer *vl = vectorLayer();
if ( vl && mIncludeRelatedAttributes )
if ( mLayer && mIncludeRelatedAttributes )
{
QList< QgsRelation > relations = QgsProject::instance()->relationManager()->referencedRelations( vl );
QList< QgsRelation > relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer.data() );
Q_FOREACH ( const QgsRelation &relation, relations )
{
if ( attributeCounter > 0 )
@@ -167,14 +176,24 @@ QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVarian
if ( childLayer )
{
QgsFeatureIterator it = childLayer->getFeatures( req );
QVector<QVariant> attributeWidgetCaches;
int fieldIndex = 0;
Q_FOREACH ( const QgsField &field, childLayer->fields() )
{
QgsEditorWidgetSetup setup = field.editorWidgetSetup();
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
attributeWidgetCaches.append( fieldFormatter->createCache( childLayer, fieldIndex, setup.config() ) );
fieldIndex++;
}

QgsFeature relatedFet;
int relationFeatures = 0;
while ( it.nextFeature( relatedFet ) )
{
if ( relationFeatures > 0 )
relatedFeatureAttributes += QLatin1String( ",\n" );

relatedFeatureAttributes += QgsJSONUtils::exportAttributes( relatedFet );
relatedFeatureAttributes += QgsJsonUtils::exportAttributes( relatedFet, childLayer, attributeWidgetCaches );
relationFeatures++;
}
}
@@ -204,7 +223,7 @@ QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVarian
return s;
}

QString QgsJSONExporter::exportFeatures( const QgsFeatureList &features ) const
QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features ) const
{
QStringList featureJSON;
Q_FOREACH ( const QgsFeature &feature, features )
@@ -218,20 +237,20 @@ QString QgsJSONExporter::exportFeatures( const QgsFeatureList &features ) const


//
// QgsJSONUtils
// QgsJsonUtils
//

QgsFeatureList QgsJSONUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
QgsFeatureList QgsJsonUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
{
return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
}

QgsFields QgsJSONUtils::stringToFields( const QString &string, QTextCodec *encoding )
QgsFields QgsJsonUtils::stringToFields( const QString &string, QTextCodec *encoding )
{
return QgsOgrUtils::stringToFields( string, encoding );
}

QString QgsJSONUtils::encodeValue( const QVariant &value )
QString QgsJsonUtils::encodeValue( const QVariant &value )
{
if ( value.isNull() )
return QStringLiteral( "null" );
@@ -268,7 +287,7 @@ QString QgsJSONUtils::encodeValue( const QVariant &value )
}
}

QString QgsJSONUtils::exportAttributes( const QgsFeature &feature )
QString QgsJsonUtils::exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer, QVector<QVariant> attributeWidgetCaches )
{
QgsFields fields = feature.fields();
QString attrs;
@@ -278,12 +297,21 @@ QString QgsJSONUtils::exportAttributes( const QgsFeature &feature )
attrs += QLatin1String( ",\n" );

QVariant val = feature.attributes().at( i );

if ( layer )
{
QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
}

attrs += encodeValue( fields.at( i ).name() ) + ':' + encodeValue( val );
}
return attrs.prepend( '{' ).append( '}' );
}

QVariantList QgsJSONUtils::parseArray( const QString &json, QVariant::Type type )
QVariantList QgsJsonUtils::parseArray( const QString &json, QVariant::Type type )
{
QJsonParseError error;
const QJsonDocument jsonDoc = QJsonDocument::fromJson( json.toUtf8(), &error );
@@ -21,29 +21,29 @@
#include "qgscoordinatereferencesystem.h"
#include "qgscoordinatetransform.h"
#include "qgsfields.h"
#include "qgsvectorlayer.h"

class QTextCodec;
class QgsVectorLayer;

/** \ingroup core
* \class QgsJSONExporter
* \class QgsJsonExporter
* \brief Handles exporting QgsFeature features to GeoJSON features.
*
* Note that geometries will be automatically reprojected to WGS84 to match GeoJSON spec
* if either the source vector layer or source CRS is set.
* \since QGIS 2.16
*/

class CORE_EXPORT QgsJSONExporter
class CORE_EXPORT QgsJsonExporter
{
public:

/** Constructor for QgsJSONExporter.
/** Constructor for QgsJsonExporter.
* \param vectorLayer associated vector layer (required for related attribute export)
* \param precision maximum number of decimal places to use for geometry coordinates,
* the RFC 7946 GeoJSON specification recommends limiting coordinate precision to 6
*/
QgsJSONExporter( const QgsVectorLayer *vectorLayer = nullptr, int precision = 6 );
QgsJsonExporter( QgsVectorLayer *vectorLayer = nullptr, int precision = 6 );

/** Sets the maximum number of decimal places to use in geometry coordinates.
* The RFC 7946 GeoJSON specification recommends limiting coordinate precision to 6
@@ -97,7 +97,7 @@ class CORE_EXPORT QgsJSONExporter
* \param vectorLayer vector layer
* \see vectorLayer()
*/
void setVectorLayer( const QgsVectorLayer *vectorLayer );
void setVectorLayer( QgsVectorLayer *vectorLayer );

/** Returns the associated vector layer, if set.
* \see setVectorLayer()
@@ -193,8 +193,8 @@ class CORE_EXPORT QgsJSONExporter
//! Whether to include attributes from related features in JSON export
bool mIncludeRelatedAttributes;

//! Layer ID of associated vector layer. Required for related attribute export.
QString mLayerId;
//! Associated vector layer. Required for related attribute export.
QPointer< QgsVectorLayer > mLayer;

QgsCoordinateReferenceSystem mCrs;

@@ -203,12 +203,12 @@ class CORE_EXPORT QgsJSONExporter
};

/** \ingroup core
* \class QgsJSONUtils
* \class QgsJsonUtils
* \brief Helper utilities for working with JSON and GeoJSON conversions.
* \since QGIS 2.16
*/

class CORE_EXPORT QgsJSONUtils
class CORE_EXPORT QgsJsonUtils
{
public:

@@ -240,8 +240,13 @@ class CORE_EXPORT QgsJSONUtils

/** Exports all attributes from a QgsFeature as a JSON map type.
* \param feature feature to export
* \param layer optional associated vector layer. If specified, this allows
* richer export utilising settings like the layer's fields widget configuration.
* \param attributeWidgetCaches optional widget configuration cache. Can be used
* to speed up exporting the attributes for multiple features from the same layer.
*/
static QString exportAttributes( const QgsFeature &feature );
static QString exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer = nullptr,
QVector<QVariant> attributeWidgetCaches = QVector<QVariant>() );

/** Parse a simple array (depth=1).
* \param json the JSON to parse
@@ -554,7 +554,7 @@ QVariant QgsSpatiaLiteFeatureIterator::getFeatureAttribute( sqlite3_stmt *stmt,
if ( type == QVariant::List || type == QVariant::StringList )
{
// assume arrays are stored as JSON
QVariant result = QVariant( QgsJSONUtils::parseArray( txt, subType ) );
QVariant result = QVariant( QgsJsonUtils::parseArray( txt, subType ) );
result.convert( type );
return result;
}

0 comments on commit 86ce441

Please sign in to comment.