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