mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-29 00:03:59 -04:00
505 lines
17 KiB
C++
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;
|
|
}
|