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