18#include <nlohmann/json.hpp>
40#include <QJsonDocument>
43#include "moc_qgsjsonutils.cpp"
52 mTransform.setSourceCrs( mCrs );
57 mTransform.setDestinationCrs( mDestinationCrs );
66 mTransform.setSourceCrs( mCrs );
78 mTransform.setSourceCrs( mCrs );
87 const QVariant &
id,
int indent )
const
93 catch ( json::type_error &ex )
95 QgsLogger::warning( QStringLiteral(
"Cannot export feature to json: %1" ).arg( ex.what() ) );
98 catch ( json::other_error &ex )
100 QgsLogger::warning( QStringLiteral(
"Cannot export feature to json: %1" ).arg( ex.what() ) );
109 {
"type",
"Feature" },
114 auto intId =
id.toLongLong( &ok );
117 featureJson[
"id"] = intId;
121 featureJson[
"id"] =
id.toString().toStdString();
126 featureJson[
"id"] =
nullptr;
130 featureJson[
"id"] = feature.
id();
134 if ( !geom.
isNull() && mIncludeGeometry )
136 if ( mCrs.isValid() )
153 featureJson[
"bbox" ] =
161 featureJson[
"geometry" ] = geom.
asJsonObject( mPrecision );
165 featureJson[
"geometry" ] =
nullptr;
170 if ( mIncludeAttributes || !extraProperties.isEmpty() )
173 if ( mIncludeAttributes )
177 QStringList formattersAllowList;
178 formattersAllowList << QStringLiteral(
"KeyValue" )
179 << QStringLiteral(
"List" )
180 << QStringLiteral(
"ValueRelation" )
181 << QStringLiteral(
"ValueMap" );
183 for (
int i = 0; i < fields.
count(); ++i )
185 if ( ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) || mExcludedAttributeIndexes.contains( i ) )
190 if ( mUseFieldFormatters && mLayer )
194 if ( formattersAllowList.contains( fieldFormatter->
id() ) )
198 QString name = fields.
at( i ).
name();
199 if ( mAttributeDisplayName )
201 name = mLayer->attributeDisplayName( i );
207 if ( !extraProperties.isEmpty() )
209 QVariantMap::const_iterator it = extraProperties.constBegin();
210 for ( ; it != extraProperties.constEnd(); ++it )
217 if ( mLayer && mIncludeRelatedAttributes )
220 for (
const auto &relation : std::as_const( relations ) )
225 json relatedFeatureAttributes;
229 QVector<QVariant> attributeWidgetCaches;
232 for (
const QgsField &field : fields )
236 attributeWidgetCaches.append( fieldFormatter->
createCache( childLayer, fieldIndex, setup.
config() ) );
245 properties[ relation.name().toStdString() ] = relatedFeatureAttributes;
249 featureJson[
"properties" ] = properties;
262 {
"type",
"FeatureCollection" },
263 {
"features", json::array() }
268 for (
const QgsFeature &feature : std::as_const( features ) )
277 mDestinationCrs = destinationCrs;
278 mTransform.setDestinationCrs( mDestinationCrs );
288 encoding = QTextCodec::codecForName(
"UTF-8" );
296 encoding = QTextCodec::codecForName(
"UTF-8" );
304 return QStringLiteral(
"null" );
306 switch ( value.userType() )
308 case QMetaType::Type::Int:
309 case QMetaType::Type::UInt:
310 case QMetaType::Type::LongLong:
311 case QMetaType::Type::ULongLong:
312 case QMetaType::Type::Double:
313 return value.toString();
315 case QMetaType::Type::Bool:
316 return value.toBool() ?
"true" :
"false";
318 case QMetaType::Type::QStringList:
319 case QMetaType::Type::QVariantList:
320 case QMetaType::Type::QVariantMap:
321 return QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson( QJsonDocument::Compact ) );
324 case QMetaType::Type::QString:
325 QString v = value.toString()
326 .replace(
'\\', QLatin1String(
"\\\\" ) )
327 .replace(
'"', QLatin1String(
"\\\"" ) )
328 .replace(
'\r', QLatin1String(
"\\r" ) )
329 .replace(
'\b', QLatin1String(
"\\b" ) )
330 .replace(
'\t', QLatin1String(
"\\t" ) )
331 .replace(
'/', QLatin1String(
"\\/" ) )
332 .replace(
'\n', QLatin1String(
"\\n" ) );
334 return v.prepend(
'"' ).append(
'"' );
342 for (
int i = 0; i < fields.
count(); ++i )
345 attrs += QLatin1String(
",\n" );
354 val = fieldFormatter->
representValue( layer, i, setup.
config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
359 return attrs.prepend(
'{' ).
append(
'}' );
364 QString errorMessage;
368 const auto jObj( json::parse( json.toStdString() ) );
369 if ( ! jObj.is_array() )
371 throw json::parse_error::create( 0, 0, QStringLiteral(
"JSON value must be an array" ).toStdString(), &jObj );
373 for (
const auto &item : jObj )
377 if ( item.is_number_integer() )
381 else if ( item.is_number_unsigned() )
383 v = item.get<
unsigned>();
385 else if ( item.is_number_float() )
388 v = item.get<
double>();
390 else if ( item.is_string() )
392 v = QString::fromStdString( item.get<std::string>() );
394 else if ( item.is_boolean() )
396 v = item.get<
bool>();
398 else if ( item.is_null() )
405 if ( type != QMetaType::Type::UnknownType )
407 if ( ! v.convert(
static_cast<int>( type ) ) )
409 QgsLogger::warning( QStringLiteral(
"Cannot convert json array element to specified type, ignoring: %1" ).arg( v.toString() ) );
413 result.push_back( v );
418 result.push_back( v );
422 catch ( json::parse_error &ex )
424 errorMessage = ex.what();
425 QgsLogger::warning( QStringLiteral(
"Cannot parse json (%1): %2" ).arg( ex.what(), json ) );
438 if ( !coords.is_array() || coords.size() < 2 || coords.size() > 3 )
440 QgsDebugError( QStringLiteral(
"JSON Point geometry coordinates must be an array of two or three numbers" ) );
444 const double x = coords[0].get<
double >();
445 const double y = coords[1].get<
double >();
446 if ( coords.size() == 2 )
448 return std::make_unique< QgsPoint >( x, y );
452 const double z = coords[2].get<
double >();
453 return std::make_unique< QgsPoint >( x, y, z );
459 if ( !coords.is_array() || coords.size() < 2 )
461 QgsDebugError( QStringLiteral(
"JSON LineString geometry coordinates must be an array of at least two points" ) );
465 const std::size_t coordsSize = coords.size();
470#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
471 x.resize(
static_cast< int >( coordsSize ) );
472 y.resize(
static_cast< int >( coordsSize ) );
473 z.resize(
static_cast< int >( coordsSize ) );
475 x.resize( coordsSize );
476 y.resize( coordsSize );
477 z.resize( coordsSize );
479 double *xOut = x.data();
480 double *yOut = y.data();
481 double *zOut = z.data();
483 for (
const auto &coord : coords )
485 if ( !coord.is_array() || coord.size() < 2 || coord.size() > 3 )
487 QgsDebugError( QStringLiteral(
"JSON LineString geometry coordinates must be an array of two or three numbers" ) );
491 *xOut++ = coord[0].get<
double >();
492 *yOut++ = coord[1].get<
double >();
493 if ( coord.size() == 3 )
495 *zOut++ = coord[2].get<
double >();
500 *zOut++ = std::numeric_limits< double >::quiet_NaN();
504 return std::make_unique< QgsLineString >( x, y, hasZ ? z : QVector<double>() );
509 if ( !coords.is_array() || coords.size() < 1 )
511 QgsDebugError( QStringLiteral(
"JSON Polygon geometry coordinates must be an array" ) );
515 const std::size_t coordsSize = coords.size();
522 auto polygon = std::make_unique< QgsPolygon >( exterior.release() );
523 for ( std::size_t i = 1; i < coordsSize; ++i )
530 polygon->addInteriorRing( ring.release() );
537 if ( !geometry.is_object() )
539 QgsDebugError( QStringLiteral(
"JSON geometry value must be an object" ) );
543 if ( !geometry.contains(
"type" ) )
545 QgsDebugError( QStringLiteral(
"JSON geometry must contain 'type'" ) );
549 const QString type = QString::fromStdString( geometry[
"type"].get< std::string >() );
550 if ( type.compare( QLatin1String(
"Point" ), Qt::CaseInsensitive ) == 0 )
552 if ( !geometry.contains(
"coordinates" ) )
554 QgsDebugError( QStringLiteral(
"JSON Point geometry must contain 'coordinates'" ) );
558 const json &coords = geometry[
"coordinates"];
561 else if ( type.compare( QLatin1String(
"MultiPoint" ), Qt::CaseInsensitive ) == 0 )
563 if ( !geometry.contains(
"coordinates" ) )
565 QgsDebugError( QStringLiteral(
"JSON MultiPoint geometry must contain 'coordinates'" ) );
569 const json &coords = geometry[
"coordinates"];
571 if ( !coords.is_array() )
573 QgsDebugError( QStringLiteral(
"JSON MultiPoint geometry coordinates must be an array" ) );
577 auto multiPoint = std::make_unique< QgsMultiPoint >();
578 multiPoint->reserve(
static_cast< int >( coords.size() ) );
579 for (
const auto &pointCoords : coords )
586 multiPoint->addGeometry( point.release() );
591 else if ( type.compare( QLatin1String(
"LineString" ), Qt::CaseInsensitive ) == 0 )
593 if ( !geometry.contains(
"coordinates" ) )
595 QgsDebugError( QStringLiteral(
"JSON LineString geometry must contain 'coordinates'" ) );
599 const json &coords = geometry[
"coordinates"];
602 else if ( type.compare( QLatin1String(
"MultiLineString" ), Qt::CaseInsensitive ) == 0 )
604 if ( !geometry.contains(
"coordinates" ) )
606 QgsDebugError( QStringLiteral(
"JSON MultiLineString geometry must contain 'coordinates'" ) );
610 const json &coords = geometry[
"coordinates"];
612 if ( !coords.is_array() )
614 QgsDebugError( QStringLiteral(
"JSON MultiLineString geometry coordinates must be an array" ) );
618 auto multiLineString = std::make_unique< QgsMultiLineString >();
619 multiLineString->reserve(
static_cast< int >( coords.size() ) );
620 for (
const auto &lineCoords : coords )
627 multiLineString->addGeometry( line.release() );
630 return multiLineString;
632 else if ( type.compare( QLatin1String(
"Polygon" ), Qt::CaseInsensitive ) == 0 )
634 if ( !geometry.contains(
"coordinates" ) )
636 QgsDebugError( QStringLiteral(
"JSON Polygon geometry must contain 'coordinates'" ) );
640 const json &coords = geometry[
"coordinates"];
641 if ( !coords.is_array() || coords.size() < 1 )
643 QgsDebugError( QStringLiteral(
"JSON Polygon geometry coordinates must be an array of at least one ring" ) );
649 else if ( type.compare( QLatin1String(
"MultiPolygon" ), Qt::CaseInsensitive ) == 0 )
651 if ( !geometry.contains(
"coordinates" ) )
653 QgsDebugError( QStringLiteral(
"JSON MultiPolygon geometry must contain 'coordinates'" ) );
657 const json &coords = geometry[
"coordinates"];
659 if ( !coords.is_array() )
661 QgsDebugError( QStringLiteral(
"JSON MultiPolygon geometry coordinates must be an array" ) );
665 auto multiPolygon = std::make_unique< QgsMultiPolygon >();
666 multiPolygon->reserve(
static_cast< int >( coords.size() ) );
667 for (
const auto &polygonCoords : coords )
674 multiPolygon->addGeometry( polygon.release() );
679 else if ( type.compare( QLatin1String(
"GeometryCollection" ), Qt::CaseInsensitive ) == 0 )
681 if ( !geometry.contains(
"geometries" ) )
683 QgsDebugError( QStringLiteral(
"JSON GeometryCollection geometry must contain 'geometries'" ) );
687 const json &geometries = geometry[
"geometries"];
689 if ( !geometries.is_array() )
691 QgsDebugError( QStringLiteral(
"JSON GeometryCollection geometries must be an array" ) );
695 auto collection = std::make_unique< QgsGeometryCollection >();
696 collection->reserve(
static_cast< int >( geometries.size() ) );
697 for (
const auto &geometry : geometries )
704 collection->addGeometry(
object.release() );
710 QgsDebugError( QStringLiteral(
"Unhandled GeoJSON geometry type: %1" ).arg( type ) );
716 if ( !geometry.is_object() )
718 QgsDebugError( QStringLiteral(
"JSON geometry value must be an object" ) );
729 const auto jObj( json::parse( geometry.toStdString() ) );
732 catch ( json::parse_error &ex )
734 QgsDebugError( QStringLiteral(
"Cannot parse json (%1): %2" ).arg( geometry, ex.what() ) );
746 if ( val.userType() == QMetaType::Type::QVariantMap )
748 const QVariantMap &vMap = val.toMap();
749 json jMap = json::object();
750 for (
auto it = vMap.constBegin(); it != vMap.constEnd(); it++ )
756 else if ( val.userType() == QMetaType::Type::QVariantList || val.userType() == QMetaType::Type::QStringList )
758 const QVariantList &vList = val.toList();
759 json jList = json::array();
760 for (
const auto &v : vList )
768 switch ( val.userType() )
771 case QMetaType::UInt:
772 case QMetaType::LongLong:
773 case QMetaType::ULongLong:
774 j = val.toLongLong();
776 case QMetaType::Double:
777 case QMetaType::Float:
780 case QMetaType::Bool:
783 case QMetaType::QByteArray:
784 j = val.toByteArray().toBase64().toStdString();
787 j = val.toString().toStdString();
797 const QVariant res =
parseJson( jsonString, error );
799 if ( !error.isEmpty() )
802 QString::fromStdString( jsonString ) ) );
812 const json j = json::parse( jsonString );
815 catch ( json::parse_error &ex )
817 error = QString::fromStdString( ex.what() );
819 catch ( json::type_error &ex )
821 error = QString::fromStdString( ex.what() );
829 bool isPrimitive =
true;
831 std::function<QVariant( json )> _parser { [ & ]( json jObj ) -> QVariant {
833 if ( jObj.is_array() )
836 QVariantList results;
837 results.reserve( jObj.size() );
838 for (
const auto &item : jObj )
840 results.push_back( _parser( item ) );
844 else if ( jObj.is_object() )
848 for (
const auto &item : jObj.items() )
850 const auto key { QString::fromStdString( item.key() ) };
851 const auto value { _parser( item.value() ) };
852 results[ key ] = value;
858 if ( jObj.is_number_unsigned() )
862 const qulonglong num { jObj.get<qulonglong>() };
863 if ( num <= std::numeric_limits<int>::max() )
865 result =
static_cast<int>( num );
867 else if ( num <= std::numeric_limits<qlonglong>::max() )
869 result =
static_cast<qlonglong
>( num );
876 else if ( jObj.is_number_integer() )
878 const qlonglong num { jObj.get<qlonglong>() };
879 if ( num <= std::numeric_limits<int>::max() && num >= std::numeric_limits<int>::lowest() )
881 result =
static_cast<int>( num );
888 else if ( jObj.is_boolean() )
890 result = jObj.get<
bool>();
892 else if ( jObj.is_number_float() )
895 result = jObj.get<
double>();
897 else if ( jObj.is_string() )
899 if ( isPrimitive && jObj.get<std::string>().length() == 0 )
901 result = QString::fromStdString( jObj.get<std::string>() ).append(
"\"" ).insert( 0,
"\"" );
905 result = QString::fromStdString( jObj.get<std::string>() );
908 else if ( jObj.is_null() )
917 return _parser( value );
922 return jsonString.isEmpty() ? QVariant() :
parseJson( jsonString.toStdString() );
929 for (
int i = 0; i < fields.
count(); ++i )
933 if ( layer && useFieldFormatters )
938 val = fieldFormatter->
representValue( layer, i, setup.
config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
949 if ( crs.
authid() ==
"OGC:CRS84" || crs.
authid() ==
"EPSG:4326" )
952 value[
"crs"][
"type"] =
"name";
953 value[
"crs"][
"properties"][
"name"] = crs.
toOgcUrn().toStdString();
@ Success
Operation succeeded.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Represents a coordinate reference system (CRS).
QString toOgcUrn() const
Returns the crs as OGC URN (format: urn:ogc:def:crs:OGC:1.3:CRS84) Returns an empty string on failure...
Custom exception class for Coordinate Reference System related exceptions.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Encapsulate a field in an attribute table or data source.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Container of fields for a vector layer.
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
virtual json asJsonObject(int precision=17) const
Exports the geometry to a json object.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
json exportFeatureToJsonObject(const QgsFeature &feature, const QVariantMap &extraProperties=QVariantMap(), const QVariant &id=QVariant()) const
Returns a QJsonObject representation of a feature.
json exportFeaturesToJsonObject(const QgsFeatureList &features) const
Returns a JSON object representation of a list of features (feature collection).
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source CRS for feature geometries.
void setDestinationCrs(const QgsCoordinateReferenceSystem &destinationCrs)
Set the destination CRS for feature geometry transformation to destinationCrs, this defaults to EPSG:...
QgsVectorLayer * vectorLayer() const
Returns the associated vector layer, if set.
QString exportFeature(const QgsFeature &feature, const QVariantMap &extraProperties=QVariantMap(), const QVariant &id=QVariant(), int indent=-1) const
Returns a GeoJSON string representation of a feature.
int precision() const
Returns the maximum number of decimal places to use in geometry coordinates.
QString exportFeatures(const QgsFeatureList &features, int indent=-1) const
Returns a GeoJSON string representation of a list of features (feature collection).
void setVectorLayer(QgsVectorLayer *vectorLayer)
Sets the associated vector layer (required for related attribute export).
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source CRS for feature geometries.
QgsJsonExporter(QgsVectorLayer *vectorLayer=nullptr, int precision=6)
Constructor for QgsJsonExporter.
static QgsGeometry geometryFromGeoJson(const json &geometry)
Parses a GeoJSON "geometry" value to a QgsGeometry object.
static QString exportAttributes(const QgsFeature &feature, QgsVectorLayer *layer=nullptr, const QVector< QVariant > &attributeWidgetCaches=QVector< QVariant >())
Exports all attributes from a QgsFeature as a JSON map type.
static QgsFeatureList stringToFeatureList(const QString &string, const QgsFields &fields=QgsFields(), QTextCodec *encoding SIP_PYARGREMOVE6=nullptr)
Attempts to parse a GeoJSON string to a collection of features.
static Q_INVOKABLE QString encodeValue(const QVariant &value)
Encodes a value to a JSON string representation, adding appropriate quotations and escaping where req...
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned and ...
static void addCrsInfo(json &value, const QgsCoordinateReferenceSystem &crs)
Add crs information entry in json object regarding old GeoJSON specification format if it differs fro...
static Q_INVOKABLE QVariantList parseArray(const QString &json, QMetaType::Type type=QMetaType::Type::UnknownType)
Parse a simple array (depth=1).
static json exportAttributesToJsonObject(const QgsFeature &feature, QgsVectorLayer *layer=nullptr, const QVector< QVariant > &attributeWidgetCaches=QVector< QVariant >(), bool useFieldFormatters=true)
Exports all attributes from a QgsFeature as a json object.
static QVariant jsonToVariant(const json &value)
Converts a JSON value to a QVariant, in case of parsing error an invalid QVariant is returned.
static QgsFields stringToFields(const QString &string, QTextCodec *encoding SIP_PYARGREMOVE6=nullptr)
Attempts to retrieve the fields from a GeoJSON string representing a collection of features.
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
static void warning(const QString &msg)
Goes to qWarning.
static QgsFeatureList stringToFeatureList(const QString &string, const QgsFields &fields, QTextCodec *encoding)
Attempts to parse a string representing a collection of features using OGR.
static QgsFields stringToFields(const QString &string, QTextCodec *encoding)
Attempts to retrieve the fields from a string representing a collection of features using OGR.
QgsRelationManager * relationManager
static QgsProject * instance()
Returns the QgsProject singleton instance.
A rectangle specified with double values.
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
static QMetaType::Type variantTypeToMetaType(QVariant::Type variantType)
Converts a QVariant::Type to a QMetaType::Type.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
Represents a vector layer which manages a vector based dataset.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
QList< QgsFeature > QgsFeatureList
std::unique_ptr< QgsPoint > parsePointFromGeoJson(const json &coords)
std::unique_ptr< QgsPolygon > parsePolygonFromGeoJson(const json &coords)
std::unique_ptr< QgsAbstractGeometry > parseGeometryFromGeoJson(const json &geometry)
std::unique_ptr< QgsLineString > parseLineStringFromGeoJson(const json &coords)
#define QgsDebugError(str)