Skip to content

Commit

Permalink
Merge pull request #4627 from nyalldawson/json_format
Browse files Browse the repository at this point in the history
Use field formatter when exporting feature attributes to JSON
  • Loading branch information
nyalldawson committed May 26, 2017
2 parents 9cfe70c + 5b0bc93 commit 86ce441
Show file tree
Hide file tree
Showing 13 changed files with 207 additions and 99 deletions.
2 changes: 1 addition & 1 deletion .ci/travis/linux/blacklist.txt
@@ -1,5 +1,5 @@
# black list
PyQgsJSONUtils
PyQgsJsonUtils
PyQgsLocalServer
PyQgsPalLabelingServer
qgis_composermapgridtest
Expand Down
2 changes: 2 additions & 0 deletions doc/api_break.dox
Expand Up @@ -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
Expand Down
17 changes: 11 additions & 6 deletions python/core/qgsjsonutils.sip
Expand Up @@ -10,7 +10,7 @@



class QgsJSONExporter
class QgsJsonExporter
{
%Docstring
Handles exporting QgsFeature features to GeoJSON features.
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -199,7 +199,7 @@ class QgsJSONExporter
};


class QgsJSONUtils
class QgsJsonUtils
{
%Docstring
Helper utilities for working with JSON and GeoJSON conversions.
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsclipboard.cpp
Expand Up @@ -142,7 +142,7 @@ QString QgsClipboard::generateClipboardText() const
}
case GeoJSON:
{
QgsJSONExporter exporter;
QgsJsonExporter exporter;
exporter.setSourceCrs( mCRS );
return exporter.exportFeatures( mFeatureClipboard );
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/composer/qgscomposerhtml.cpp
Expand Up @@ -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 );
}
Expand Down
76 changes: 52 additions & 24 deletions src/core/qgsjsonutils.cpp
Expand Up @@ -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 )
{
Expand All @@ -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 )
Expand Down Expand Up @@ -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 )
{
Expand All @@ -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;
}
Expand All @@ -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 )
Expand All @@ -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++;
}
}
Expand Down Expand Up @@ -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 )
Expand All @@ -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" );
Expand Down Expand Up @@ -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;
Expand All @@ -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 );
Expand Down
27 changes: 16 additions & 11 deletions src/core/qgsjsonutils.h
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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;

Expand All @@ -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:

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/providers/spatialite/qgsspatialitefeatureiterator.cpp
Expand Up @@ -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;
}
Expand Down

0 comments on commit 86ce441

Please sign in to comment.