QGIS/src/core/qgsjsonutils.cpp
Alessandro Pasotti be8822ed8b Precision and 3d
2019-04-16 14:00:59 +02:00

505 lines
17 KiB
C++

/***************************************************************************
qgsjsonutils.h
-------------
Date : May 206
Copyright : (C) 2016 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsjsonutils.h"
#include "qgsfeatureiterator.h"
#include "qgsogrutils.h"
#include "qgsgeometry.h"
#include "qgsvectorlayer.h"
#include "qgsrelation.h"
#include "qgsrelationmanager.h"
#include "qgsproject.h"
#include "qgsexception.h"
#include "qgslogger.h"
#include "qgsfieldformatterregistry.h"
#include "qgsfieldformatter.h"
#include "qgsapplication.h"
#include <QJsonDocument>
#include <QJsonArray>
QgsJsonExporter::QgsJsonExporter( QgsVectorLayer *vectorLayer, int precision )
: mPrecision( precision )
, mLayer( vectorLayer )
{
if ( vectorLayer )
{
mCrs = vectorLayer->crs();
mTransform.setSourceCrs( mCrs );
}
mTransform.setDestinationCrs( QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ) );
}
void QgsJsonExporter::setVectorLayer( QgsVectorLayer *vectorLayer )
{
mLayer = vectorLayer;
if ( vectorLayer )
{
mCrs = vectorLayer->crs();
mTransform.setSourceCrs( mCrs );
}
}
QgsVectorLayer *QgsJsonExporter::vectorLayer() const
{
return mLayer.data();
}
void QgsJsonExporter::setSourceCrs( const QgsCoordinateReferenceSystem &crs )
{
mCrs = crs;
mTransform.setSourceCrs( mCrs );
}
QgsCoordinateReferenceSystem QgsJsonExporter::sourceCrs() const
{
return mCrs;
}
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 ) );
QgsGeometry geom = feature.geometry();
if ( !geom.isNull() && mIncludeGeometry )
{
if ( mCrs.isValid() )
{
try
{
QgsGeometry transformed = geom;
if ( transformed.transform( mTransform ) == 0 )
geom = transformed;
}
catch ( QgsCsException &cse )
{
Q_UNUSED( cse );
}
}
QgsRectangle box = geom.boundingBox();
if ( QgsWkbTypes::flatType( geom.wkbType() ) != QgsWkbTypes::Point )
{
s += QStringLiteral( " \"bbox\":[%1, %2, %3, %4],\n" ).arg( qgsDoubleToString( box.xMinimum(), mPrecision ),
qgsDoubleToString( box.yMinimum(), mPrecision ),
qgsDoubleToString( box.xMaximum(), mPrecision ),
qgsDoubleToString( box.yMaximum(), mPrecision ) );
}
s += QLatin1String( " \"geometry\":\n " );
s += geom.asJson( mPrecision );
s += QLatin1String( ",\n" );
}
else
{
s += QLatin1String( " \"geometry\":null,\n" );
}
// build up properties element
QString properties;
int attributeCounter = 0;
if ( mIncludeAttributes || !extraProperties.isEmpty() )
{
//read all attribute values from the feature
if ( mIncludeAttributes )
{
QgsFields fields = mLayer ? mLayer->fields() : feature.fields();
// List of formatters through we want to pass the values
QStringList formattersWhiteList;
formattersWhiteList << QStringLiteral( "KeyValue" )
<< QStringLiteral( "List" )
<< QStringLiteral( "ValueRelation" )
<< QStringLiteral( "ValueMap" );
for ( int i = 0; i < fields.count(); ++i )
{
if ( ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) || mExcludedAttributeIndexes.contains( i ) )
continue;
if ( attributeCounter > 0 )
properties += QLatin1String( ",\n" );
QVariant val = feature.attributes().at( i );
if ( mLayer )
{
QgsEditorWidgetSetup setup = fields.at( i ).editorWidgetSetup();
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
if ( formattersWhiteList.contains( fieldFormatter->id() ) )
val = fieldFormatter->representValue( mLayer.data(), i, setup.config(), QVariant(), val );
}
QString name = fields.at( i ).name();
if ( mAttributeDisplayName )
{
name = mLayer->attributeDisplayName( i );
}
properties += QStringLiteral( " \"%1\":%2" ).arg( name, QgsJsonUtils::encodeValue( val ) );
++attributeCounter;
}
}
if ( !extraProperties.isEmpty() )
{
QVariantMap::const_iterator it = extraProperties.constBegin();
for ( ; it != extraProperties.constEnd(); ++it )
{
if ( attributeCounter > 0 )
properties += QLatin1String( ",\n" );
properties += QStringLiteral( " \"%1\":%2" ).arg( it.key(), QgsJsonUtils::encodeValue( it.value() ) );
++attributeCounter;
}
}
// related attributes
if ( mLayer && mIncludeRelatedAttributes )
{
QList< QgsRelation > relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer.data() );
const auto constRelations = relations;
for ( const QgsRelation &relation : constRelations )
{
if ( attributeCounter > 0 )
properties += QLatin1String( ",\n" );
QgsFeatureRequest req = relation.getRelatedFeaturesRequest( feature );
req.setFlags( QgsFeatureRequest::NoGeometry );
QgsVectorLayer *childLayer = relation.referencingLayer();
QString relatedFeatureAttributes;
if ( childLayer )
{
QgsFeatureIterator it = childLayer->getFeatures( req );
QVector<QVariant> attributeWidgetCaches;
int fieldIndex = 0;
const QgsFields fields = childLayer->fields();
for ( const QgsField &field : 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, childLayer, attributeWidgetCaches );
relationFeatures++;
}
}
relatedFeatureAttributes.prepend( '[' ).append( ']' );
properties += QStringLiteral( " \"%1\":%2" ).arg( relation.name(), relatedFeatureAttributes );
attributeCounter++;
}
}
}
bool hasProperties = attributeCounter > 0;
s += QLatin1String( " \"properties\":" );
if ( hasProperties )
{
//read all attribute values from the feature
s += "{\n" + properties + "\n }\n";
}
else
{
s += QLatin1String( "null\n" );
}
s += '}';
return s;
}
QJsonObject QgsJsonExporter::exportFeatureToJsonObject( const QgsFeature &feature, const QVariantMap &extraProperties, const QVariant &id ) const
{
QJsonObject featureJson
{
{ QStringLiteral( "type" ), QStringLiteral( "Feature" ) },
{ QStringLiteral( "id" ), ( ! id.isValid() ? QJsonValue( feature.id() ) : QJsonValue::fromVariant( id ) ) },
};
QgsGeometry geom = feature.geometry();
if ( !geom.isNull() && mIncludeGeometry )
{
if ( mCrs.isValid() )
{
try
{
QgsGeometry transformed = geom;
if ( transformed.transform( mTransform ) == 0 )
geom = transformed;
}
catch ( QgsCsException &cse )
{
Q_UNUSED( cse );
}
}
QgsRectangle box = geom.boundingBox();
if ( QgsWkbTypes::flatType( geom.wkbType() ) != QgsWkbTypes::Point )
{
featureJson[ QStringLiteral( "bbox" ) ] = QJsonArray( { box.xMinimum(),
box.yMinimum(),
box.xMaximum(),
box.yMaximum() } );
}
featureJson[ QStringLiteral( "geometry" ) ] = geom.asJsonObject( mPrecision );
}
else
{
featureJson[ QStringLiteral( "geometry" ) ] = QJsonValue();
}
// build up properties element
int attributeCounter { 0 };
QJsonObject properties;
if ( mIncludeAttributes || !extraProperties.isEmpty() )
{
//read all attribute values from the feature
if ( mIncludeAttributes )
{
QgsFields fields = mLayer ? mLayer->fields() : feature.fields();
// List of formatters through we want to pass the values
QStringList formattersWhiteList;
formattersWhiteList << QStringLiteral( "KeyValue" )
<< QStringLiteral( "List" )
<< QStringLiteral( "ValueRelation" )
<< QStringLiteral( "ValueMap" );
for ( int i = 0; i < fields.count(); ++i )
{
if ( ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) || mExcludedAttributeIndexes.contains( i ) )
continue;
QVariant val = feature.attributes().at( i );
if ( mLayer )
{
QgsEditorWidgetSetup setup = fields.at( i ).editorWidgetSetup();
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
if ( formattersWhiteList.contains( fieldFormatter->id() ) )
val = fieldFormatter->representValue( mLayer.data(), i, setup.config(), QVariant(), val );
}
QString name = fields.at( i ).name();
if ( mAttributeDisplayName )
{
name = mLayer->attributeDisplayName( i );
}
properties[ name ] = QJsonValue::fromVariant( val );
attributeCounter++;
}
}
if ( !extraProperties.isEmpty() )
{
QVariantMap::const_iterator it = extraProperties.constBegin();
for ( ; it != extraProperties.constEnd(); ++it )
{
properties[ it.key() ] = QJsonValue::fromVariant( it.value() );
attributeCounter++;
}
}
// related attributes
if ( mLayer && mIncludeRelatedAttributes )
{
QList< QgsRelation > relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer.data() );
for ( const auto &relation : qgis::as_const( relations ) )
{
QgsFeatureRequest req = relation.getRelatedFeaturesRequest( feature );
req.setFlags( QgsFeatureRequest::NoGeometry );
QgsVectorLayer *childLayer = relation.referencingLayer();
QJsonArray relatedFeatureAttributes;
if ( childLayer )
{
QgsFeatureIterator it = childLayer->getFeatures( req );
QVector<QVariant> attributeWidgetCaches;
int fieldIndex = 0;
const QgsFields fields { childLayer->fields() };
for ( const QgsField &field : fields )
{
QgsEditorWidgetSetup setup = field.editorWidgetSetup();
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
attributeWidgetCaches.append( fieldFormatter->createCache( childLayer, fieldIndex, setup.config() ) );
fieldIndex++;
}
QgsFeature relatedFet;
while ( it.nextFeature( relatedFet ) )
{
relatedFeatureAttributes += QgsJsonUtils::exportAttributes( relatedFet, childLayer, attributeWidgetCaches );
}
}
properties[ relation.name() ] = relatedFeatureAttributes;
attributeCounter++;
}
}
}
// bool hasProperties = attributeCounter > 0;
featureJson[ QStringLiteral( "properties" ) ] = properties;
return featureJson;
}
QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features ) const
{
QStringList featureJSON;
const auto constFeatures = features;
for ( const QgsFeature &feature : constFeatures )
{
featureJSON << exportFeature( feature );
}
return QStringLiteral( "{ \"type\": \"FeatureCollection\",\n \"features\":[\n%1\n]}" ).arg( featureJSON.join( QStringLiteral( ",\n" ) ) );
}
//
// QgsJsonUtils
//
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 )
{
return QgsOgrUtils::stringToFields( string, encoding );
}
QString QgsJsonUtils::encodeValue( const QVariant &value )
{
if ( value.isNull() )
return QStringLiteral( "null" );
switch ( value.type() )
{
case QVariant::Int:
case QVariant::UInt:
case QVariant::LongLong:
case QVariant::ULongLong:
case QVariant::Double:
return value.toString();
case QVariant::Bool:
return value.toBool() ? "true" : "false";
case QVariant::StringList:
case QVariant::List:
case QVariant::Map:
return QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson( QJsonDocument::Compact ) );
default:
case QVariant::String:
QString v = value.toString()
.replace( '\\', QLatin1String( "\\\\" ) )
.replace( '"', QLatin1String( "\\\"" ) )
.replace( '\r', QLatin1String( "\\r" ) )
.replace( '\b', QLatin1String( "\\b" ) )
.replace( '\t', QLatin1String( "\\t" ) )
.replace( '/', QLatin1String( "\\/" ) )
.replace( '\n', QLatin1String( "\\n" ) );
return v.prepend( '"' ).append( '"' );
}
}
QString QgsJsonUtils::exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
{
QgsFields fields = feature.fields();
QString attrs;
for ( int i = 0; i < fields.count(); ++i )
{
if ( i > 0 )
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 )
{
QJsonParseError error;
const QJsonDocument jsonDoc = QJsonDocument::fromJson( json.toUtf8(), &error );
QVariantList result;
if ( error.error != QJsonParseError::NoError )
{
QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( error.errorString(), json ) );
return result;
}
if ( !jsonDoc.isArray() )
{
QgsLogger::warning( QStringLiteral( "Cannot parse json (%1) as array: %2" ).arg( error.errorString(), json ) );
return result;
}
const auto constArray = jsonDoc.array();
for ( const QJsonValue &cur : constArray )
{
QVariant curVariant = cur.toVariant();
if ( curVariant.convert( type ) )
result.append( curVariant );
else
QgsLogger::warning( QStringLiteral( "Cannot convert json array element: %1" ).arg( cur.toString() ) );
}
return result;
}
QJsonObject QgsJsonUtils::exportAttributesToJsonObject( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
{
QgsFields fields = feature.fields();
QJsonObject attrs;
for ( int i = 0; i < fields.count(); ++i )
{
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.insert( fields.at( i ).name(), QJsonValue::fromVariant( val ) );
}
return attrs;
}