36#include <QJsonDocument>
39#include <nlohmann/json.hpp>
43 , mLayer( vectorLayer )
83 const QVariant &
id,
int indent )
const
92 {
"type",
"Feature" },
97 auto intId =
id.toLongLong( &ok );
100 featureJson[
"id"] = intId;
104 featureJson[
"id"] =
id.toString().toStdString();
109 featureJson[
"id"] =
nullptr;
113 featureJson[
"id"] = feature.
id();
117 if ( !geom.
isNull() && mIncludeGeometry )
136 featureJson[
"bbox" ] =
144 featureJson[
"geometry" ] = geom.
asJsonObject( mPrecision );
148 featureJson[
"geometry" ] =
nullptr;
153 if ( mIncludeAttributes || !extraProperties.isEmpty() )
156 if ( mIncludeAttributes )
160 QStringList formattersAllowList;
161 formattersAllowList << QStringLiteral(
"KeyValue" )
162 << QStringLiteral(
"List" )
163 << QStringLiteral(
"ValueRelation" )
164 << QStringLiteral(
"ValueMap" );
166 for (
int i = 0; i < fields.
count(); ++i )
168 if ( ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) || mExcludedAttributeIndexes.contains( i ) )
177 if ( formattersAllowList.contains( fieldFormatter->
id() ) )
181 QString name = fields.
at( i ).
name();
182 if ( mAttributeDisplayName )
184 name = mLayer->attributeDisplayName( i );
190 if ( !extraProperties.isEmpty() )
192 QVariantMap::const_iterator it = extraProperties.constBegin();
193 for ( ; it != extraProperties.constEnd(); ++it )
200 if ( mLayer && mIncludeRelatedAttributes )
203 for (
const auto &relation : std::as_const( relations ) )
208 json relatedFeatureAttributes;
212 QVector<QVariant> attributeWidgetCaches;
215 for (
const QgsField &field : fields )
219 attributeWidgetCaches.append( fieldFormatter->
createCache( childLayer, fieldIndex, setup.
config() ) );
228 properties[ relation.name().toStdString() ] = relatedFeatureAttributes;
232 featureJson[
"properties" ] = properties;
245 {
"type",
"FeatureCollection" },
246 {
"features", json::array() }
248 for (
const QgsFeature &feature : std::as_const( features ) )
257 mDestinationCrs = destinationCrs;
268 encoding = QTextCodec::codecForName(
"UTF-8" );
276 encoding = QTextCodec::codecForName(
"UTF-8" );
284 return QStringLiteral(
"null" );
286 switch ( value.type() )
290 case QVariant::LongLong:
291 case QVariant::ULongLong:
292 case QVariant::Double:
293 return value.toString();
296 return value.toBool() ?
"true" :
"false";
298 case QVariant::StringList:
301 return QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson( QJsonDocument::Compact ) );
304 case QVariant::String:
305 QString v = value.toString()
306 .replace(
'\\', QLatin1String(
"\\\\" ) )
307 .replace(
'"', QLatin1String(
"\\\"" ) )
308 .replace(
'\r', QLatin1String(
"\\r" ) )
309 .replace(
'\b', QLatin1String(
"\\b" ) )
310 .replace(
'\t', QLatin1String(
"\\t" ) )
311 .replace(
'/', QLatin1String(
"\\/" ) )
312 .replace(
'\n', QLatin1String(
"\\n" ) );
314 return v.prepend(
'"' ).append(
'"' );
322 for (
int i = 0; i < fields.
count(); ++i )
325 attrs += QLatin1String(
",\n" );
334 val = fieldFormatter->
representValue( layer, i, setup.
config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
339 return attrs.prepend(
'{' ).append(
'}' );
344 QString errorMessage;
348 const auto jObj( json::parse( json.toStdString() ) );
349 if ( ! jObj.is_array() )
351 throw json::parse_error::create( 0, 0, QStringLiteral(
"JSON value must be an array" ).toStdString() );
353 for (
const auto &item : jObj )
357 if ( item.is_number_integer() )
361 else if ( item.is_number_unsigned() )
363 v = item.get<
unsigned>();
365 else if ( item.is_number_float() )
368 v = item.get<
double>();
370 else if ( item.is_string() )
372 v = QString::fromStdString( item.get<std::string>() );
374 else if ( item.is_boolean() )
376 v = item.get<
bool>();
378 else if ( item.is_null() )
381 v = QVariant( type == QVariant::Type::Invalid ? QVariant::Type::Int : type );
385 if ( type != QVariant::Invalid )
387 if ( ! v.convert(
static_cast<int>( type ) ) )
389 QgsLogger::warning( QStringLiteral(
"Cannot convert json array element to specified type, ignoring: %1" ).arg( v.toString() ) );
393 result.push_back( v );
398 result.push_back( v );
402 catch ( json::parse_error &ex )
404 errorMessage = ex.what();
405 QgsLogger::warning( QStringLiteral(
"Cannot parse json (%1): %2" ).arg( ex.what(), json ) );
413 if ( !coords.is_array() || coords.size() < 2 || coords.size() > 3 )
415 QgsDebugError( QStringLiteral(
"JSON Point geometry coordinates must be an array of two or three numbers" ) );
419 const double x = coords[0].get<
double >();
420 const double y = coords[1].get<
double >();
421 if ( coords.size() == 2 )
423 return std::make_unique< QgsPoint >( x, y );
427 const double z = coords[2].get<
double >();
428 return std::make_unique< QgsPoint >( x, y, z );
434 if ( !coords.is_array() || coords.size() < 2 )
436 QgsDebugError( QStringLiteral(
"JSON LineString geometry coordinates must be an array of at least two points" ) );
440 const std::size_t coordsSize = coords.size();
445#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
446 x.resize(
static_cast< int >( coordsSize ) );
447 y.resize(
static_cast< int >( coordsSize ) );
448 z.resize(
static_cast< int >( coordsSize ) );
450 x.resize( coordsSize );
451 y.resize( coordsSize );
452 z.resize( coordsSize );
454 double *xOut = x.data();
455 double *yOut = y.data();
456 double *zOut = z.data();
458 for (
const auto &coord : coords )
460 if ( !coord.is_array() || coord.size() < 2 || coord.size() > 3 )
462 QgsDebugError( QStringLiteral(
"JSON LineString geometry coordinates must be an array of two or three numbers" ) );
466 *xOut++ = coord[0].get<
double >();
467 *yOut++ = coord[1].get<
double >();
468 if ( coord.size() == 3 )
470 *zOut++ = coord[2].get<
double >();
475 *zOut++ = std::numeric_limits< double >::quiet_NaN();
479 return std::make_unique< QgsLineString >( x, y, hasZ ? z : QVector<double>() );
484 if ( !coords.is_array() || coords.size() < 1 )
486 QgsDebugError( QStringLiteral(
"JSON Polygon geometry coordinates must be an array" ) );
490 const std::size_t coordsSize = coords.size();
497 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >( exterior.release() );
498 for ( std::size_t i = 1; i < coordsSize; ++i )
505 polygon->addInteriorRing( ring.release() );
512 if ( !geometry.is_object() )
514 QgsDebugError( QStringLiteral(
"JSON geometry value must be an object" ) );
518 if ( !geometry.contains(
"type" ) )
520 QgsDebugError( QStringLiteral(
"JSON geometry must contain 'type'" ) );
524 const QString type = QString::fromStdString( geometry[
"type"].get< std::string >() );
525 if ( type.compare( QLatin1String(
"Point" ), Qt::CaseInsensitive ) == 0 )
527 if ( !geometry.contains(
"coordinates" ) )
529 QgsDebugError( QStringLiteral(
"JSON Point geometry must contain 'coordinates'" ) );
533 const json &coords = geometry[
"coordinates"];
536 else if ( type.compare( QLatin1String(
"MultiPoint" ), Qt::CaseInsensitive ) == 0 )
538 if ( !geometry.contains(
"coordinates" ) )
540 QgsDebugError( QStringLiteral(
"JSON MultiPoint geometry must contain 'coordinates'" ) );
544 const json &coords = geometry[
"coordinates"];
546 if ( !coords.is_array() )
548 QgsDebugError( QStringLiteral(
"JSON MultiPoint geometry coordinates must be an array" ) );
552 std::unique_ptr< QgsMultiPoint > multiPoint = std::make_unique< QgsMultiPoint >();
553 multiPoint->reserve(
static_cast< int >( coords.size() ) );
554 for (
const auto &pointCoords : coords )
561 multiPoint->addGeometry( point.release() );
566 else if ( type.compare( QLatin1String(
"LineString" ), Qt::CaseInsensitive ) == 0 )
568 if ( !geometry.contains(
"coordinates" ) )
570 QgsDebugError( QStringLiteral(
"JSON LineString geometry must contain 'coordinates'" ) );
574 const json &coords = geometry[
"coordinates"];
577 else if ( type.compare( QLatin1String(
"MultiLineString" ), Qt::CaseInsensitive ) == 0 )
579 if ( !geometry.contains(
"coordinates" ) )
581 QgsDebugError( QStringLiteral(
"JSON MultiLineString geometry must contain 'coordinates'" ) );
585 const json &coords = geometry[
"coordinates"];
587 if ( !coords.is_array() )
589 QgsDebugError( QStringLiteral(
"JSON MultiLineString geometry coordinates must be an array" ) );
593 std::unique_ptr< QgsMultiLineString > multiLineString = std::make_unique< QgsMultiLineString >();
594 multiLineString->reserve(
static_cast< int >( coords.size() ) );
595 for (
const auto &lineCoords : coords )
602 multiLineString->addGeometry( line.release() );
605 return multiLineString;
607 else if ( type.compare( QLatin1String(
"Polygon" ), Qt::CaseInsensitive ) == 0 )
609 if ( !geometry.contains(
"coordinates" ) )
611 QgsDebugError( QStringLiteral(
"JSON Polygon geometry must contain 'coordinates'" ) );
615 const json &coords = geometry[
"coordinates"];
616 if ( !coords.is_array() || coords.size() < 1 )
618 QgsDebugError( QStringLiteral(
"JSON Polygon geometry coordinates must be an array of at least one ring" ) );
624 else if ( type.compare( QLatin1String(
"MultiPolygon" ), Qt::CaseInsensitive ) == 0 )
626 if ( !geometry.contains(
"coordinates" ) )
628 QgsDebugError( QStringLiteral(
"JSON MultiPolygon geometry must contain 'coordinates'" ) );
632 const json &coords = geometry[
"coordinates"];
634 if ( !coords.is_array() )
636 QgsDebugError( QStringLiteral(
"JSON MultiPolygon geometry coordinates must be an array" ) );
640 std::unique_ptr< QgsMultiPolygon > multiPolygon = std::make_unique< QgsMultiPolygon >();
641 multiPolygon->reserve(
static_cast< int >( coords.size() ) );
642 for (
const auto &polygonCoords : coords )
649 multiPolygon->addGeometry( polygon.release() );
654 else if ( type.compare( QLatin1String(
"GeometryCollection" ), Qt::CaseInsensitive ) == 0 )
656 if ( !geometry.contains(
"geometries" ) )
658 QgsDebugError( QStringLiteral(
"JSON GeometryCollection geometry must contain 'geometries'" ) );
662 const json &geometries = geometry[
"geometries"];
664 if ( !geometries.is_array() )
666 QgsDebugError( QStringLiteral(
"JSON GeometryCollection geometries must be an array" ) );
670 std::unique_ptr< QgsGeometryCollection > collection = std::make_unique< QgsGeometryCollection >();
671 collection->reserve(
static_cast< int >( geometries.size() ) );
672 for (
const auto &geometry : geometries )
679 collection->addGeometry(
object.release() );
685 QgsDebugError( QStringLiteral(
"Unhandled GeoJSON geometry type: %1" ).arg( type ) );
691 if ( !geometry.is_object() )
693 QgsDebugError( QStringLiteral(
"JSON geometry value must be an object" ) );
704 const auto jObj( json::parse( geometry.toStdString() ) );
707 catch ( json::parse_error &ex )
709 QgsDebugError( QStringLiteral(
"Cannot parse json (%1): %2" ).arg( geometry, ex.what() ) );
721 if ( val.type() == QVariant::Type::Map )
723 const QVariantMap &vMap = val.toMap();
724 json jMap = json::object();
725 for (
auto it = vMap.constBegin(); it != vMap.constEnd(); it++ )
731 else if ( val.type() == QVariant::Type::List || val.type() == QVariant::Type::StringList )
733 const QVariantList &vList = val.toList();
734 json jList = json::array();
735 for (
const auto &v : vList )
743 switch ( val.userType() )
746 case QMetaType::UInt:
747 case QMetaType::LongLong:
748 case QMetaType::ULongLong:
749 j = val.toLongLong();
751 case QMetaType::Double:
752 case QMetaType::Float:
755 case QMetaType::Bool:
758 case QMetaType::QByteArray:
759 j = val.toByteArray().toBase64().toStdString();
762 j = val.toString().toStdString();
772 const QVariant res =
parseJson( jsonString, error );
774 if ( !error.isEmpty() )
777 QString::fromStdString( jsonString ) ) );
785 bool isPrimitive =
true;
787 std::function<QVariant( json )> _parser { [ & ]( json jObj ) -> QVariant {
789 if ( jObj.is_array() )
792 QVariantList results;
793 results.reserve( jObj.size() );
794 for (
const auto &item : jObj )
796 results.push_back( _parser( item ) );
800 else if ( jObj.is_object() )
804 for (
const auto &item : jObj.items() )
806 const auto key { QString::fromStdString( item.key() ) };
807 const auto value { _parser( item.value() ) };
808 results[ key ] = value;
814 if ( jObj.is_number_unsigned() )
818 const qulonglong num { jObj.get<qulonglong>() };
819 if ( num <= std::numeric_limits<int>::max() )
821 result =
static_cast<int>( num );
823 else if ( num <= std::numeric_limits<qlonglong>::max() )
825 result =
static_cast<qlonglong
>( num );
832 else if ( jObj.is_number_integer() )
834 const qlonglong num { jObj.get<qlonglong>() };
835 if ( num <= std::numeric_limits<int>::max() && num >= std::numeric_limits<int>::lowest() )
837 result =
static_cast<int>( num );
844 else if ( jObj.is_boolean() )
846 result = jObj.get<
bool>();
848 else if ( jObj.is_number_float() )
851 result = jObj.get<
double>();
853 else if ( jObj.is_string() )
855 if ( isPrimitive && jObj.get<std::string>().length() == 0 )
857 result = QString::fromStdString( jObj.get<std::string>() ).append(
"\"" ).insert( 0,
"\"" );
861 result = QString::fromStdString( jObj.get<std::string>() );
864 else if ( jObj.is_null() )
876 const json j = json::parse( jsonString );
879 catch ( json::parse_error &ex )
881 error = QString::fromStdString( ex.what() );
888 return parseJson( jsonString.toStdString() );
895 for (
int i = 0; i < fields.
count(); ++i )
904 val = fieldFormatter->
representValue( layer, i, setup.
config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
@ Success
Operation succeeded.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
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)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
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.
int count() const
Returns number of items.
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.
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 QgsFeatureList stringToFeatureList(const QString &string, const QgsFields &fields=QgsFields(), QTextCodec *encoding=nullptr)
Attempts to parse a GeoJSON string to a collection of features.
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 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 json exportAttributesToJsonObject(const QgsFeature &feature, QgsVectorLayer *layer=nullptr, const QVector< QVariant > &attributeWidgetCaches=QVector< QVariant >())
Exports all attributes from a QgsFeature as a json object.
static QgsFields stringToFields(const QString &string, QTextCodec *encoding=nullptr)
Attempts to retrieve the fields from a GeoJSON string representing a collection of features.
static Q_INVOKABLE QVariantList parseArray(const QString &json, QVariant::Type type=QVariant::Invalid)
Parse a simple array (depth=1)
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
static void warning(const QString &msg)
Goes to qWarning.
QgsCoordinateReferenceSystem crs
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.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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)
const QgsCoordinateReferenceSystem & crs