QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
qgsjsonutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsjsonutils.h
3 -------------
4 Date : May 206
5 Copyright : (C) 2016 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsjsonutils.h"
17#include "moc_qgsjsonutils.cpp"
18#include "qgsfeatureiterator.h"
19#include "qgsogrutils.h"
20#include "qgsgeometry.h"
21#include "qgsvectorlayer.h"
22#include "qgsrelation.h"
23#include "qgsrelationmanager.h"
24#include "qgsproject.h"
25#include "qgsexception.h"
26#include "qgslogger.h"
28#include "qgsfieldformatter.h"
29#include "qgsapplication.h"
30#include "qgsfeatureid.h"
31#include "qgslinestring.h"
32#include "qgsmultipoint.h"
33#include "qgsmultilinestring.h"
34#include "qgspolygon.h"
35#include "qgsmultipolygon.h"
36
37#include <QJsonDocument>
38#include <QJsonArray>
39#include <QTextCodec>
40#include <nlohmann/json.hpp>
41
43 : mPrecision( precision )
44 , mLayer( vectorLayer )
45{
46 if ( vectorLayer )
47 {
48 mCrs = vectorLayer->crs();
49 mTransform.setSourceCrs( mCrs );
50 }
51
52 // Default 4326
53 mDestinationCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );
54 mTransform.setDestinationCrs( mDestinationCrs );
55}
56
58{
59 mLayer = vectorLayer;
60 if ( vectorLayer )
61 {
62 mCrs = vectorLayer->crs();
63 mTransform.setSourceCrs( mCrs );
64 }
65}
66
68{
69 return mLayer.data();
70}
71
73{
74 mCrs = crs;
75 mTransform.setSourceCrs( mCrs );
76}
77
82
83QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties,
84 const QVariant &id, int indent ) const
85{
86 return QString::fromStdString( exportFeatureToJsonObject( feature, extraProperties, id ).dump( indent ) );
87}
88
89json QgsJsonExporter::exportFeatureToJsonObject( const QgsFeature &feature, const QVariantMap &extraProperties, const QVariant &id ) const
90{
91 json featureJson
92 {
93 { "type", "Feature" },
94 };
95 if ( id.isValid() )
96 {
97 bool ok = false;
98 auto intId = id.toLongLong( &ok );
99 if ( ok )
100 {
101 featureJson["id"] = intId;
102 }
103 else
104 {
105 featureJson["id"] = id.toString().toStdString();
106 }
107 }
108 else if ( FID_IS_NULL( feature.id() ) )
109 {
110 featureJson["id"] = nullptr;
111 }
112 else
113 {
114 featureJson["id"] = feature.id();
115 }
116
117 QgsGeometry geom = feature.geometry();
118 if ( !geom.isNull() && mIncludeGeometry )
119 {
120 if ( mCrs.isValid() )
121 {
122 try
123 {
124 QgsGeometry transformed = geom;
125 if ( mTransformGeometries && transformed.transform( mTransform ) == Qgis::GeometryOperationResult::Success )
126 geom = transformed;
127 }
128 catch ( QgsCsException &cse )
129 {
130 Q_UNUSED( cse )
131 }
132 }
133 QgsRectangle box = geom.boundingBox();
134
136 {
137 featureJson[ "bbox" ] =
138 {
139 qgsRound( box.xMinimum(), mPrecision ),
140 qgsRound( box.yMinimum(), mPrecision ),
141 qgsRound( box.xMaximum(), mPrecision ),
142 qgsRound( box.yMaximum(), mPrecision )
143 };
144 }
145 featureJson[ "geometry" ] = geom.asJsonObject( mPrecision );
146 }
147 else
148 {
149 featureJson[ "geometry" ] = nullptr;
150 }
151
152 // build up properties element
153 json properties;
154 if ( mIncludeAttributes || !extraProperties.isEmpty() )
155 {
156 //read all attribute values from the feature
157 if ( mIncludeAttributes )
158 {
159 QgsFields fields = mLayer ? mLayer->fields() : feature.fields();
160 // List of formatters through we want to pass the values
161 QStringList formattersAllowList;
162 formattersAllowList << QStringLiteral( "KeyValue" )
163 << QStringLiteral( "List" )
164 << QStringLiteral( "ValueRelation" )
165 << QStringLiteral( "ValueMap" );
166
167 for ( int i = 0; i < fields.count(); ++i )
168 {
169 if ( ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) || mExcludedAttributeIndexes.contains( i ) )
170 continue;
171
172 QVariant val = feature.attributes().at( i );
173
174 if ( mUseFieldFormatters && mLayer )
175 {
176 const QgsEditorWidgetSetup setup = fields.at( i ).editorWidgetSetup();
178 if ( formattersAllowList.contains( fieldFormatter->id() ) )
179 val = fieldFormatter->representValue( mLayer.data(), i, setup.config(), QVariant(), val );
180 }
181
182 QString name = fields.at( i ).name();
183 if ( mAttributeDisplayName )
184 {
185 name = mLayer->attributeDisplayName( i );
186 }
187 properties[ name.toStdString() ] = QgsJsonUtils::jsonFromVariant( val );
188 }
189 }
190
191 if ( !extraProperties.isEmpty() )
192 {
193 QVariantMap::const_iterator it = extraProperties.constBegin();
194 for ( ; it != extraProperties.constEnd(); ++it )
195 {
196 properties[ it.key().toStdString() ] = QgsJsonUtils::jsonFromVariant( it.value() );
197 }
198 }
199
200 // related attributes
201 if ( mLayer && mIncludeRelatedAttributes )
202 {
203 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer.data() ); // skip-keyword-check
204 for ( const auto &relation : std::as_const( relations ) )
205 {
206 QgsFeatureRequest req = relation.getRelatedFeaturesRequest( feature );
208 QgsVectorLayer *childLayer = relation.referencingLayer();
209 json relatedFeatureAttributes;
210 if ( childLayer )
211 {
212 QgsFeatureIterator it = childLayer->getFeatures( req );
213 QVector<QVariant> attributeWidgetCaches;
214 int fieldIndex = 0;
215 const QgsFields fields { childLayer->fields() };
216 for ( const QgsField &field : fields )
217 {
218 QgsEditorWidgetSetup setup = field.editorWidgetSetup();
220 attributeWidgetCaches.append( fieldFormatter->createCache( childLayer, fieldIndex, setup.config() ) );
221 fieldIndex++;
222 }
223 QgsFeature relatedFet;
224 while ( it.nextFeature( relatedFet ) )
225 {
226 relatedFeatureAttributes += QgsJsonUtils::exportAttributesToJsonObject( relatedFet, childLayer, attributeWidgetCaches, mUseFieldFormatters );
227 }
228 }
229 properties[ relation.name().toStdString() ] = relatedFeatureAttributes;
230 }
231 }
232 }
233 featureJson[ "properties" ] = properties;
234 return featureJson;
235}
236
237QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features, int indent ) const
238{
239 return QString::fromStdString( exportFeaturesToJsonObject( features ).dump( indent ) );
240}
241
243{
244 json data
245 {
246 { "type", "FeatureCollection" },
247 { "features", json::array() }
248 };
249
250 QgsJsonUtils::addCrsInfo( data, mDestinationCrs );
251
252 for ( const QgsFeature &feature : std::as_const( features ) )
253 {
254 data["features"].push_back( exportFeatureToJsonObject( feature ) );
255 }
256 return data;
257}
258
260{
261 mDestinationCrs = destinationCrs;
262 mTransform.setDestinationCrs( mDestinationCrs );
263}
264
265//
266// QgsJsonUtils
267//
268
269QgsFeatureList QgsJsonUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
270{
271 if ( !encoding )
272 encoding = QTextCodec::codecForName( "UTF-8" );
273
274 return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
275}
276
277QgsFields QgsJsonUtils::stringToFields( const QString &string, QTextCodec *encoding )
278{
279 if ( !encoding )
280 encoding = QTextCodec::codecForName( "UTF-8" );
281
282 return QgsOgrUtils::stringToFields( string, encoding );
283}
284
285QString QgsJsonUtils::encodeValue( const QVariant &value )
286{
287 if ( QgsVariantUtils::isNull( value ) )
288 return QStringLiteral( "null" );
289
290 switch ( value.userType() )
291 {
292 case QMetaType::Type::Int:
293 case QMetaType::Type::UInt:
294 case QMetaType::Type::LongLong:
295 case QMetaType::Type::ULongLong:
296 case QMetaType::Type::Double:
297 return value.toString();
298
299 case QMetaType::Type::Bool:
300 return value.toBool() ? "true" : "false";
301
302 case QMetaType::Type::QStringList:
303 case QMetaType::Type::QVariantList:
304 case QMetaType::Type::QVariantMap:
305 return QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson( QJsonDocument::Compact ) );
306
307 default:
308 case QMetaType::Type::QString:
309 QString v = value.toString()
310 .replace( '\\', QLatin1String( "\\\\" ) )
311 .replace( '"', QLatin1String( "\\\"" ) )
312 .replace( '\r', QLatin1String( "\\r" ) )
313 .replace( '\b', QLatin1String( "\\b" ) )
314 .replace( '\t', QLatin1String( "\\t" ) )
315 .replace( '/', QLatin1String( "\\/" ) )
316 .replace( '\n', QLatin1String( "\\n" ) );
317
318 return v.prepend( '"' ).append( '"' );
319 }
320}
321
322QString QgsJsonUtils::exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
323{
324 QgsFields fields = feature.fields();
325 QString attrs;
326 for ( int i = 0; i < fields.count(); ++i )
327 {
328 if ( i > 0 )
329 attrs += QLatin1String( ",\n" );
330
331 QVariant val = feature.attributes().at( i );
332
333 if ( layer )
334 {
335 QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
337 if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
338 val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
339 }
340
341 attrs += encodeValue( fields.at( i ).name() ) + ':' + encodeValue( val );
342 }
343 return attrs.prepend( '{' ).append( '}' );
344}
345
346QVariantList QgsJsonUtils::parseArray( const QString &json, QMetaType::Type type )
347{
348 QString errorMessage;
349 QVariantList result;
350 try
351 {
352 const auto jObj( json::parse( json.toStdString() ) );
353 if ( ! jObj.is_array() )
354 {
355 throw json::parse_error::create( 0, 0, QStringLiteral( "JSON value must be an array" ).toStdString() );
356 }
357 for ( const auto &item : jObj )
358 {
359 // Create a QVariant from the array item
360 QVariant v;
361 if ( item.is_number_integer() )
362 {
363 v = item.get<int>();
364 }
365 else if ( item.is_number_unsigned() )
366 {
367 v = item.get<unsigned>();
368 }
369 else if ( item.is_number_float() )
370 {
371 // Note: it's a double and not a float on purpose
372 v = item.get<double>();
373 }
374 else if ( item.is_string() )
375 {
376 v = QString::fromStdString( item.get<std::string>() );
377 }
378 else if ( item.is_boolean() )
379 {
380 v = item.get<bool>();
381 }
382 else if ( item.is_null() )
383 {
384 // Fallback to int
385 v = QgsVariantUtils::createNullVariant( type == QMetaType::Type::UnknownType ? QMetaType::Type::Int : type );
386 }
387
388 // If a destination type was specified (it's not invalid), try to convert
389 if ( type != QMetaType::Type::UnknownType )
390 {
391 if ( ! v.convert( static_cast<int>( type ) ) )
392 {
393 QgsLogger::warning( QStringLiteral( "Cannot convert json array element to specified type, ignoring: %1" ).arg( v.toString() ) );
394 }
395 else
396 {
397 result.push_back( v );
398 }
399 }
400 else
401 {
402 result.push_back( v );
403 }
404 }
405 }
406 catch ( json::parse_error &ex )
407 {
408 errorMessage = ex.what();
409 QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( ex.what(), json ) );
410 }
411
412 return result;
413}
414
415QVariantList QgsJsonUtils::parseArray( const QString &json, QVariant::Type type )
416{
418}
419
420std::unique_ptr< QgsPoint> parsePointFromGeoJson( const json &coords )
421{
422 if ( !coords.is_array() || coords.size() < 2 || coords.size() > 3 )
423 {
424 QgsDebugError( QStringLiteral( "JSON Point geometry coordinates must be an array of two or three numbers" ) );
425 return nullptr;
426 }
427
428 const double x = coords[0].get< double >();
429 const double y = coords[1].get< double >();
430 if ( coords.size() == 2 )
431 {
432 return std::make_unique< QgsPoint >( x, y );
433 }
434 else
435 {
436 const double z = coords[2].get< double >();
437 return std::make_unique< QgsPoint >( x, y, z );
438 }
439}
440
441std::unique_ptr< QgsLineString> parseLineStringFromGeoJson( const json &coords )
442{
443 if ( !coords.is_array() || coords.size() < 2 )
444 {
445 QgsDebugError( QStringLiteral( "JSON LineString geometry coordinates must be an array of at least two points" ) );
446 return nullptr;
447 }
448
449 const std::size_t coordsSize = coords.size();
450
451 QVector< double > x;
452 QVector< double > y;
453 QVector< double > z;
454#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
455 x.resize( static_cast< int >( coordsSize ) );
456 y.resize( static_cast< int >( coordsSize ) );
457 z.resize( static_cast< int >( coordsSize ) );
458#else
459 x.resize( coordsSize );
460 y.resize( coordsSize );
461 z.resize( coordsSize );
462#endif
463 double *xOut = x.data();
464 double *yOut = y.data();
465 double *zOut = z.data();
466 bool hasZ = false;
467 for ( const auto &coord : coords )
468 {
469 if ( !coord.is_array() || coord.size() < 2 || coord.size() > 3 )
470 {
471 QgsDebugError( QStringLiteral( "JSON LineString geometry coordinates must be an array of two or three numbers" ) );
472 return nullptr;
473 }
474
475 *xOut++ = coord[0].get< double >();
476 *yOut++ = coord[1].get< double >();
477 if ( coord.size() == 3 )
478 {
479 *zOut++ = coord[2].get< double >();
480 hasZ = true;
481 }
482 else
483 {
484 *zOut++ = std::numeric_limits< double >::quiet_NaN();
485 }
486 }
487
488 return std::make_unique< QgsLineString >( x, y, hasZ ? z : QVector<double>() );
489}
490
491std::unique_ptr< QgsPolygon > parsePolygonFromGeoJson( const json &coords )
492{
493 if ( !coords.is_array() || coords.size() < 1 )
494 {
495 QgsDebugError( QStringLiteral( "JSON Polygon geometry coordinates must be an array" ) );
496 return nullptr;
497 }
498
499 const std::size_t coordsSize = coords.size();
500 std::unique_ptr< QgsLineString > exterior = parseLineStringFromGeoJson( coords[0] );
501 if ( !exterior )
502 {
503 return nullptr;
504 }
505
506 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >( exterior.release() );
507 for ( std::size_t i = 1; i < coordsSize; ++i )
508 {
509 std::unique_ptr< QgsLineString > ring = parseLineStringFromGeoJson( coords[i] );
510 if ( !ring )
511 {
512 return nullptr;
513 }
514 polygon->addInteriorRing( ring.release() );
515 }
516 return polygon;
517}
518
519std::unique_ptr< QgsAbstractGeometry > parseGeometryFromGeoJson( const json &geometry )
520{
521 if ( !geometry.is_object() )
522 {
523 QgsDebugError( QStringLiteral( "JSON geometry value must be an object" ) );
524 return nullptr;
525 }
526
527 if ( !geometry.contains( "type" ) )
528 {
529 QgsDebugError( QStringLiteral( "JSON geometry must contain 'type'" ) );
530 return nullptr;
531 }
532
533 const QString type = QString::fromStdString( geometry["type"].get< std::string >() );
534 if ( type.compare( QLatin1String( "Point" ), Qt::CaseInsensitive ) == 0 )
535 {
536 if ( !geometry.contains( "coordinates" ) )
537 {
538 QgsDebugError( QStringLiteral( "JSON Point geometry must contain 'coordinates'" ) );
539 return nullptr;
540 }
541
542 const json &coords = geometry["coordinates"];
543 return parsePointFromGeoJson( coords );
544 }
545 else if ( type.compare( QLatin1String( "MultiPoint" ), Qt::CaseInsensitive ) == 0 )
546 {
547 if ( !geometry.contains( "coordinates" ) )
548 {
549 QgsDebugError( QStringLiteral( "JSON MultiPoint geometry must contain 'coordinates'" ) );
550 return nullptr;
551 }
552
553 const json &coords = geometry["coordinates"];
554
555 if ( !coords.is_array() )
556 {
557 QgsDebugError( QStringLiteral( "JSON MultiPoint geometry coordinates must be an array" ) );
558 return nullptr;
559 }
560
561 std::unique_ptr< QgsMultiPoint > multiPoint = std::make_unique< QgsMultiPoint >();
562 multiPoint->reserve( static_cast< int >( coords.size() ) );
563 for ( const auto &pointCoords : coords )
564 {
565 std::unique_ptr< QgsPoint > point = parsePointFromGeoJson( pointCoords );
566 if ( !point )
567 {
568 return nullptr;
569 }
570 multiPoint->addGeometry( point.release() );
571 }
572
573 return multiPoint;
574 }
575 else if ( type.compare( QLatin1String( "LineString" ), Qt::CaseInsensitive ) == 0 )
576 {
577 if ( !geometry.contains( "coordinates" ) )
578 {
579 QgsDebugError( QStringLiteral( "JSON LineString geometry must contain 'coordinates'" ) );
580 return nullptr;
581 }
582
583 const json &coords = geometry["coordinates"];
584 return parseLineStringFromGeoJson( coords );
585 }
586 else if ( type.compare( QLatin1String( "MultiLineString" ), Qt::CaseInsensitive ) == 0 )
587 {
588 if ( !geometry.contains( "coordinates" ) )
589 {
590 QgsDebugError( QStringLiteral( "JSON MultiLineString geometry must contain 'coordinates'" ) );
591 return nullptr;
592 }
593
594 const json &coords = geometry["coordinates"];
595
596 if ( !coords.is_array() )
597 {
598 QgsDebugError( QStringLiteral( "JSON MultiLineString geometry coordinates must be an array" ) );
599 return nullptr;
600 }
601
602 std::unique_ptr< QgsMultiLineString > multiLineString = std::make_unique< QgsMultiLineString >();
603 multiLineString->reserve( static_cast< int >( coords.size() ) );
604 for ( const auto &lineCoords : coords )
605 {
606 std::unique_ptr< QgsLineString > line = parseLineStringFromGeoJson( lineCoords );
607 if ( !line )
608 {
609 return nullptr;
610 }
611 multiLineString->addGeometry( line.release() );
612 }
613
614 return multiLineString;
615 }
616 else if ( type.compare( QLatin1String( "Polygon" ), Qt::CaseInsensitive ) == 0 )
617 {
618 if ( !geometry.contains( "coordinates" ) )
619 {
620 QgsDebugError( QStringLiteral( "JSON Polygon geometry must contain 'coordinates'" ) );
621 return nullptr;
622 }
623
624 const json &coords = geometry["coordinates"];
625 if ( !coords.is_array() || coords.size() < 1 )
626 {
627 QgsDebugError( QStringLiteral( "JSON Polygon geometry coordinates must be an array of at least one ring" ) );
628 return nullptr;
629 }
630
631 return parsePolygonFromGeoJson( coords );
632 }
633 else if ( type.compare( QLatin1String( "MultiPolygon" ), Qt::CaseInsensitive ) == 0 )
634 {
635 if ( !geometry.contains( "coordinates" ) )
636 {
637 QgsDebugError( QStringLiteral( "JSON MultiPolygon geometry must contain 'coordinates'" ) );
638 return nullptr;
639 }
640
641 const json &coords = geometry["coordinates"];
642
643 if ( !coords.is_array() )
644 {
645 QgsDebugError( QStringLiteral( "JSON MultiPolygon geometry coordinates must be an array" ) );
646 return nullptr;
647 }
648
649 std::unique_ptr< QgsMultiPolygon > multiPolygon = std::make_unique< QgsMultiPolygon >();
650 multiPolygon->reserve( static_cast< int >( coords.size() ) );
651 for ( const auto &polygonCoords : coords )
652 {
653 std::unique_ptr< QgsPolygon > polygon = parsePolygonFromGeoJson( polygonCoords );
654 if ( !polygon )
655 {
656 return nullptr;
657 }
658 multiPolygon->addGeometry( polygon.release() );
659 }
660
661 return multiPolygon;
662 }
663 else if ( type.compare( QLatin1String( "GeometryCollection" ), Qt::CaseInsensitive ) == 0 )
664 {
665 if ( !geometry.contains( "geometries" ) )
666 {
667 QgsDebugError( QStringLiteral( "JSON GeometryCollection geometry must contain 'geometries'" ) );
668 return nullptr;
669 }
670
671 const json &geometries = geometry["geometries"];
672
673 if ( !geometries.is_array() )
674 {
675 QgsDebugError( QStringLiteral( "JSON GeometryCollection geometries must be an array" ) );
676 return nullptr;
677 }
678
679 std::unique_ptr< QgsGeometryCollection > collection = std::make_unique< QgsGeometryCollection >();
680 collection->reserve( static_cast< int >( geometries.size() ) );
681 for ( const auto &geometry : geometries )
682 {
683 std::unique_ptr< QgsAbstractGeometry > object = parseGeometryFromGeoJson( geometry );
684 if ( !object )
685 {
686 return nullptr;
687 }
688 collection->addGeometry( object.release() );
689 }
690
691 return collection;
692 }
693
694 QgsDebugError( QStringLiteral( "Unhandled GeoJSON geometry type: %1" ).arg( type ) );
695 return nullptr;
696}
697
699{
700 if ( !geometry.is_object() )
701 {
702 QgsDebugError( QStringLiteral( "JSON geometry value must be an object" ) );
703 return QgsGeometry();
704 }
705
706 return QgsGeometry( parseGeometryFromGeoJson( geometry ) );
707}
708
710{
711 try
712 {
713 const auto jObj( json::parse( geometry.toStdString() ) );
714 return geometryFromGeoJson( jObj );
715 }
716 catch ( json::parse_error &ex )
717 {
718 QgsDebugError( QStringLiteral( "Cannot parse json (%1): %2" ).arg( geometry, ex.what() ) );
719 return QgsGeometry();
720 }
721}
722
723json QgsJsonUtils::jsonFromVariant( const QVariant &val )
724{
725 if ( QgsVariantUtils::isNull( val ) )
726 {
727 return nullptr;
728 }
729 json j;
730 if ( val.userType() == QMetaType::Type::QVariantMap )
731 {
732 const QVariantMap &vMap = val.toMap();
733 json jMap = json::object();
734 for ( auto it = vMap.constBegin(); it != vMap.constEnd(); it++ )
735 {
736 jMap[ it.key().toStdString() ] = jsonFromVariant( it.value() );
737 }
738 j = jMap;
739 }
740 else if ( val.userType() == QMetaType::Type::QVariantList || val.userType() == QMetaType::Type::QStringList )
741 {
742 const QVariantList &vList = val.toList();
743 json jList = json::array();
744 for ( const auto &v : vList )
745 {
746 jList.push_back( jsonFromVariant( v ) );
747 }
748 j = jList;
749 }
750 else
751 {
752 switch ( val.userType() )
753 {
754 case QMetaType::Int:
755 case QMetaType::UInt:
756 case QMetaType::LongLong:
757 case QMetaType::ULongLong:
758 j = val.toLongLong();
759 break;
760 case QMetaType::Double:
761 case QMetaType::Float:
762 j = val.toDouble();
763 break;
764 case QMetaType::Bool:
765 j = val.toBool();
766 break;
767 case QMetaType::QByteArray:
768 j = val.toByteArray().toBase64().toStdString();
769 break;
770 default:
771 j = val.toString().toStdString();
772 break;
773 }
774 }
775 return j;
776}
777
778QVariant QgsJsonUtils::parseJson( const std::string &jsonString )
779{
780 QString error;
781 const QVariant res = parseJson( jsonString, error );
782
783 if ( !error.isEmpty() )
784 {
785 QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( error,
786 QString::fromStdString( jsonString ) ) );
787 }
788 return res;
789}
790
791QVariant QgsJsonUtils::parseJson( const std::string &jsonString, QString &error )
792{
793 error.clear();
794 try
795 {
796 const json j = json::parse( jsonString );
797 return jsonToVariant( j );
798 }
799 catch ( json::parse_error &ex )
800 {
801 error = QString::fromStdString( ex.what() );
802 }
803 return QVariant();
804}
805
806QVariant QgsJsonUtils::jsonToVariant( const json &value )
807{
808 // tracks whether entire json string is a primitive
809 bool isPrimitive = true;
810
811 std::function<QVariant( json )> _parser { [ & ]( json jObj ) -> QVariant {
812 QVariant result;
813 if ( jObj.is_array() )
814 {
815 isPrimitive = false;
816 QVariantList results;
817 results.reserve( jObj.size() );
818 for ( const auto &item : jObj )
819 {
820 results.push_back( _parser( item ) );
821 }
822 result = results;
823 }
824 else if ( jObj.is_object() )
825 {
826 isPrimitive = false;
827 QVariantMap results;
828 for ( const auto &item : jObj.items() )
829 {
830 const auto key { QString::fromStdString( item.key() ) };
831 const auto value { _parser( item.value() ) };
832 results[ key ] = value;
833 }
834 result = results;
835 }
836 else
837 {
838 if ( jObj.is_number_unsigned() )
839 {
840 // Try signed int and long long first, fall back
841 // onto unsigned long long
842 const qulonglong num { jObj.get<qulonglong>() };
843 if ( num <= std::numeric_limits<int>::max() )
844 {
845 result = static_cast<int>( num );
846 }
847 else if ( num <= std::numeric_limits<qlonglong>::max() )
848 {
849 result = static_cast<qlonglong>( num );
850 }
851 else
852 {
853 result = num;
854 }
855 }
856 else if ( jObj.is_number_integer() )
857 {
858 const qlonglong num { jObj.get<qlonglong>() };
859 if ( num <= std::numeric_limits<int>::max() && num >= std::numeric_limits<int>::lowest() )
860 {
861 result = static_cast<int>( num );
862 }
863 else
864 {
865 result = num;
866 }
867 }
868 else if ( jObj.is_boolean() )
869 {
870 result = jObj.get<bool>();
871 }
872 else if ( jObj.is_number_float() )
873 {
874 // Note: it's a double and not a float on purpose
875 result = jObj.get<double>();
876 }
877 else if ( jObj.is_string() )
878 {
879 if ( isPrimitive && jObj.get<std::string>().length() == 0 )
880 {
881 result = QString::fromStdString( jObj.get<std::string>() ).append( "\"" ).insert( 0, "\"" );
882 }
883 else
884 {
885 result = QString::fromStdString( jObj.get<std::string>() );
886 }
887 }
888 else if ( jObj.is_null() )
889 {
890 // Do nothing (leave invalid)
891 }
892 }
893 return result;
894 }
895 };
896
897 return _parser( value );
898}
899
900QVariant QgsJsonUtils::parseJson( const QString &jsonString )
901{
902 return parseJson( jsonString.toStdString() );
903}
904
905json QgsJsonUtils::exportAttributesToJsonObject( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches, bool useFieldFormatters )
906{
907 QgsFields fields = feature.fields();
908 json attrs;
909 for ( int i = 0; i < fields.count(); ++i )
910 {
911 QVariant val = feature.attributes().at( i );
912
913 if ( layer && useFieldFormatters )
914 {
915 QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
917 if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
918 val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
919 }
920 attrs[fields.at( i ).name().toStdString()] = jsonFromVariant( val );
921 }
922 return attrs;
923}
924
926{
927 // When user request EPSG:4326 we return a compliant CRS84 lon/lat GeoJSON
928 // so no need to add CRS information
929 if ( crs.authid() == "OGC:CRS84" || crs.authid() == "EPSG:4326" )
930 return;
931
932 value["crs"]["type"] = "name";
933 value["crs"]["properties"]["name"] = crs.toOgcUrn().toStdString();
934}
@ 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.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toOgcUrn() const
Returns the crs as OGC URN (format: urn:ogc:def:crs:OGC:1.3:CRS84) Returns an empty string on failure...
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
Custom exception class for Coordinate Reference System related exceptions.
Holder for the widget type and its configuration for a field.
QVariantMap config() const
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.
This class 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...
Definition qgsfeature.h:58
QgsAttributes attributes
Definition qgsfeature.h:67
QgsFields fields
Definition qgsfeature.h:68
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString id() const =0
Returns a unique id for this field formatter.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QString name
Definition qgsfield.h:62
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:740
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
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 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.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
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
Definition qgsproject.h:117
static QgsProject * instance()
Returns the QgsProject singleton instance.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
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 data sets.
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.
Definition qgis.h:5999
QList< QgsFeature > QgsFeatureList
#define FID_IS_NULL(fid)
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)
Definition qgslogger.h:38
const QgsCoordinateReferenceSystem & crs
int precision