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