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