QGIS API Documentation 4.3.0-Master (35968c35f7e)
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 "qgscircularstring.h"
22#include "qgscompoundcurve.h"
23#include "qgscurvepolygon.h"
24#include "qgsexception.h"
25#include "qgsfeatureid.h"
26#include "qgsfeatureiterator.h"
27#include "qgsfieldformatter.h"
29#include "qgsgeometry.h"
30#include "qgslinestring.h"
31#include "qgslogger.h"
32#include "qgsmulticurve.h"
33#include "qgsmultilinestring.h"
34#include "qgsmultipoint.h"
35#include "qgsmultipolygon.h"
36#include "qgsmultisurface.h"
37#include "qgsogrutils.h"
38#include "qgspolygon.h"
39#include "qgsproject.h"
40#include "qgsrelation.h"
41#include "qgsrelationmanager.h"
42#include "qgsvectorlayer.h"
43
44#include <QJsonArray>
45#include <QJsonDocument>
46#include <QString>
47#include <QTextCodec>
48
49#include "moc_qgsjsonutils.cpp"
50
51using namespace Qt::StringLiterals;
52
54 : mPrecision( precision )
55 , mLayer( vectorLayer )
56{
57 if ( vectorLayer )
58 {
59 mCrs = vectorLayer->crs();
60 mTransform.setSourceCrs( mCrs );
61 }
62
63 // Default CRS84
64 mDestinationCrs = QgsCoordinateReferenceSystem( u"OGC:CRS84"_s );
65 mTransform.setDestinationCrs( mDestinationCrs );
66}
67
69{
70 mLayer = vectorLayer;
71 if ( vectorLayer )
72 {
73 mCrs = vectorLayer->crs();
74 mTransform.setSourceCrs( mCrs );
75 }
76}
77
79{
80 return mLayer.data();
81}
82
84{
85 mCrs = crs;
86 mTransform.setSourceCrs( mCrs );
87}
88
93
94QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties, const QVariant &id, int indent, const QVariantMap &extraMembers ) const
95{
96 try
97 {
98 return QString::fromStdString( exportFeatureToJsonObject( feature, extraProperties, id, extraMembers ).dump( indent ) );
99 }
100 catch ( json::type_error &ex )
101 {
102 QgsLogger::warning( u"Cannot export feature to json: %1"_s.arg( ex.what() ) );
103 return QString();
104 }
105 catch ( json::other_error &ex )
106 {
107 QgsLogger::warning( u"Cannot export feature to json: %1"_s.arg( ex.what() ) );
108 return QString();
109 }
110}
111
112json QgsJsonExporter::exportFeatureToJsonObject( const QgsFeature &feature, const QVariantMap &extraProperties, const QVariant &id, const QVariantMap &extraMembers ) const
113{
114 // Required by RFC7946, but also needed for JSON-FG profiles if the source (or destination) CRS is not CRS84
115 QgsCoordinateTransform transformToCRS84 { mTransform };
116 transformToCRS84.setDestinationCrs( QgsCoordinateReferenceSystem( u"OGC:CRS84"_s ) );
117
118 const bool destinationCrsIsRfc7946Compliant = mDestinationCrs.authid() == "OGC:CRS84" || mDestinationCrs.authid() == "EPSG:4326" || mDestinationCrs.authid() == "CRS:84";
119 const bool sourceCrsIsRfc7946Compliant = ( mCrs.authid() == "OGC:CRS84" || mCrs.authid() == "CRS:84" || mCrs.authid() == "EPSG:4326" );
120 // Output requires CRS84 coordinates if the GeoJSON profile is RFC7946 or JSON-FG+ (which requires RFC7946 compliance for geometries) or if the requested CRS is CRS84
121 const bool requiresCRS84geom = ( mGeoJsonProfile == Qgis::GeoJsonProfile::Rfc7946 || mGeoJsonProfile == Qgis::GeoJsonProfile::JsonFgPlus )
122 || ( mTransformGeometries && destinationCrsIsRfc7946Compliant );
123
124 // Already transformed
125 const bool geomRequiresTransformToDestinationCrs = mCrs != mDestinationCrs;
126
127 QgsGeometry geom = feature.geometry();
128
129 const bool includeGeometryInformation { !geom.isNull() && mIncludeGeometry };
130 const Qgis::WkbType flatType = QgsWkbTypes::flatType( feature.geometry().wkbType() );
131 const bool featureHasM { QgsWkbTypes::hasM( geom.wkbType() ) };
132 const bool mainGeometryIsRfc7946Compliant {
133 !featureHasM
134 && destinationCrsIsRfc7946Compliant
136 };
137
138 json featureJson {
139 { "type", "Feature" },
140 };
141
142 if ( !mOmitCollectionLevelInformation && ( mGeoJsonProfile == Qgis::GeoJsonProfile::JsonFgPlus || mGeoJsonProfile == Qgis::GeoJsonProfile::JsonFg ) )
143 {
144 featureJson["conformsTo"] = { "http://www.opengis.net/spec/json-fg-1/1.0/conf/core" };
145 if ( includeGeometryInformation )
146 {
147 if ( featureHasM )
148 {
149 featureJson["conformsTo"].push_back( "http://www.opengis.net/spec/json-fg-1/1.0/conf/measures" );
150 featureJson["measures"] = { { "enabled", true } };
151 }
152 QgsJsonUtils::addCrsInfo( featureJson, mDestinationCrs, mGeoJsonProfile );
153 }
154 }
155
156 //foreign members
157 if ( !extraMembers.isEmpty() )
158 {
159 QVariantMap::const_iterator it = extraMembers.constBegin();
160 for ( ; it != extraMembers.constEnd(); ++it )
161 {
162 featureJson[it.key().toStdString()] = QgsJsonUtils::jsonFromVariant( it.value() );
163 }
164 }
165
166 if ( id.isValid() )
167 {
168 bool ok = false;
169 auto intId = id.toLongLong( &ok );
170 if ( ok )
171 {
172 featureJson["id"] = intId;
173 }
174 else
175 {
176 featureJson["id"] = id.toString().toStdString();
177 }
178 }
179 else if ( FID_IS_NULL( feature.id() ) )
180 {
181 featureJson["id"] = nullptr;
182 }
183 else
184 {
185 featureJson["id"] = feature.id();
186 }
187
188 // ////////////////////////////////////////
189 // Geometry handling
190
191 if ( includeGeometryInformation )
192 {
193 // If it is JSON-FG plus we need both CRS84 and the requested CRS
194 QgsGeometry transformedCRS84Geom = geom;
195
196 if ( requiresCRS84geom )
197 {
198 if ( sourceCrsIsRfc7946Compliant )
199 {
200 transformedCRS84Geom = geom;
201 }
202 else
203 {
204 try
205 {
206 transformedCRS84Geom.transform( transformToCRS84 );
207 }
208 catch ( QgsCsException &cse )
209 {
210 Q_UNUSED( cse )
211 }
212 }
213 }
214
215 // Do we need to transform the main geometry to the destination CRS?
216 if ( mGeoJsonProfile != Qgis::GeoJsonProfile::Rfc7946 && geomRequiresTransformToDestinationCrs )
217 {
218 if ( destinationCrsIsRfc7946Compliant )
219 {
220 geom = transformedCRS84Geom;
221 }
222 else if ( mCrs.isValid() )
223 {
224 try
225 {
226 geom.transform( mTransform );
227 }
228 catch ( QgsCsException &cse )
229 {
230 Q_UNUSED( cse )
231 }
232 }
233 }
234
235
236 std::function<void( json & )> invertCoords;
237 invertCoords = [&invertCoords]( json &geometry ) {
238 if ( geometry.contains( "coordinates" ) )
239 {
240 invertCoords( geometry["coordinates"] );
241 }
242 else if ( geometry.contains( "geometries" ) )
243 {
244 for ( json &geom : geometry["geometries"] )
245 {
246 invertCoords( geom );
247 }
248 }
249 else if ( geometry.is_array() && geometry.size() > 0 )
250 {
251 if ( geometry[0].is_array() )
252 {
253 for ( json &geom : geometry )
254 {
255 invertCoords( geom );
256 }
257 }
258 else
259 {
260 if ( geometry.size() >= 2 && geometry[0].is_number() && geometry[1].is_number() )
261 {
262 std::swap( geometry[0], geometry[1] );
263 }
264 }
265 }
266 };
267
268 auto addBbox = [&featureJson, &flatType, this]( const QgsGeometry &geometry ) {
269 if ( flatType == Qgis::WkbType::Point )
270 {
271 // For points, the bbox is just the coordinates of the point (or the points)
272 return;
273 }
274 auto bbox = geometry.boundingBox();
275 featureJson["bbox"]
276 = { qgsRound( bbox.xMinimum(), this->mPrecision ), qgsRound( bbox.yMinimum(), this->mPrecision ), qgsRound( bbox.xMaximum(), this->mPrecision ), qgsRound( bbox.yMaximum(), this->mPrecision ) };
277 };
278
279 switch ( mGeoJsonProfile )
280 {
282 {
283 featureJson["geometry"] = transformedCRS84Geom.asJsonObject( mPrecision, Qgis::GeoJsonProfile::Rfc7946 );
284 addBbox( transformedCRS84Geom );
285 break;
286 }
288 {
289 featureJson["geometry"] = geom.asJsonObject( mPrecision, Qgis::GeoJsonProfile::Rfc7946 );
290 addBbox( geom );
291 break;
292 }
294 {
295 // See: https://docs.ogc.org/is/21-045r1/21-045r1.html#_use_of_geometry_andor_place
296 // If the geometry is a valid GeoJSON geometry (that is, conformant to the GeoJSON RFC7946 specification),
297 // the geometry is encoded as the value of the "geometry" member. The "place" member then has the value null or is omitted.
298
299 // PLUS version: always add the fallback geometry as "geometry" member, so that the output is always compliant with RFC7946.
300 // If the geometry is not compliant with RFC7946 also add the original geometry as "place" member.
301 featureJson["geometry"] = transformedCRS84Geom.asJsonObject( mPrecision, Qgis::GeoJsonProfile::Rfc7946 );
302 addBbox( transformedCRS84Geom );
303 if ( !mainGeometryIsRfc7946Compliant )
304 {
305 json place = geom.asJsonObject( mPrecision, mGeoJsonProfile );
306 if ( mDestinationCrs.hasAxisInverted() )
307 {
308 invertCoords( place );
309 }
310 featureJson["place"] = place;
311 }
312 break;
313 }
315 {
316 // See: https://docs.ogc.org/is/21-045r1/21-045r1.html#_use_of_geometry_andor_place
317 // Add "geometry" or "place" depending on whether the geometry is compliant with RFC7946 or not.
318 // In the latter case, the "geometry" member is omitted.
319 if ( !mainGeometryIsRfc7946Compliant )
320 {
321 json place = geom.asJsonObject( mPrecision, mGeoJsonProfile );
322 if ( mDestinationCrs.hasAxisInverted() )
323 {
324 invertCoords( place );
325 }
326 featureJson["place"] = place;
327 }
328 else
329 {
330 featureJson["geometry"] = geom.asJsonObject( mPrecision, mGeoJsonProfile );
331 }
332 break;
333 }
334 }
335 }
336 else
337 {
338 featureJson["geometry"] = nullptr;
339 }
340
341 // build up properties element
342 json properties;
343 if ( mIncludeAttributes || !extraProperties.isEmpty() )
344 {
345 //read all attribute values from the feature
346 if ( mIncludeAttributes )
347 {
348 QgsFields fields = mLayer ? mLayer->fields() : feature.fields();
349 // List of formatters through we want to pass the values
350 QStringList formattersAllowList;
351 formattersAllowList << u"KeyValue"_s << u"List"_s << u"ValueRelation"_s << u"ValueMap"_s;
352
353 for ( int i = 0; i < fields.count(); ++i )
354 {
355 if ( ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) || mExcludedAttributeIndexes.contains( i ) )
356 continue;
357
358 QVariant val = feature.attributes().at( i );
359
360 if ( mUseFieldFormatters && mLayer )
361 {
362 const QgsEditorWidgetSetup setup = fields.at( i ).editorWidgetSetup();
364 if ( formattersAllowList.contains( fieldFormatter->id() ) )
365 val = fieldFormatter->representValue( mLayer.data(), i, setup.config(), QVariant(), val );
366 }
367
368 QString name = fields.at( i ).name();
369 if ( mAttributeDisplayName )
370 {
371 name = mLayer->attributeDisplayName( i );
372 }
373 properties[name.toStdString()] = QgsJsonUtils::jsonFromVariant( val );
374 }
375 }
376
377 if ( !extraProperties.isEmpty() )
378 {
379 QVariantMap::const_iterator it = extraProperties.constBegin();
380 for ( ; it != extraProperties.constEnd(); ++it )
381 {
382 properties[it.key().toStdString()] = QgsJsonUtils::jsonFromVariant( it.value() );
383 }
384 }
385
386 // related attributes
387 if ( mLayer && mIncludeRelatedAttributes )
388 {
389 QList< QgsRelation > relations;
390
391 if ( QgsProject *project = mLayer->project() )
392 {
393 relations = project->relationManager()->referencedRelations( mLayer.data() );
394 }
395
396 for ( const auto &relation : std::as_const( relations ) )
397 {
398 QgsFeatureRequest req = relation.getRelatedFeaturesRequest( feature );
400 QgsVectorLayer *childLayer = relation.referencingLayer();
401 json relatedFeatureAttributes;
402 if ( childLayer )
403 {
404 QgsFeatureIterator it = childLayer->getFeatures( req );
405 QVector<QVariant> attributeWidgetCaches;
406 int fieldIndex = 0;
407 const QgsFields fields { childLayer->fields() };
408 for ( const QgsField &field : fields )
409 {
410 QgsEditorWidgetSetup setup = field.editorWidgetSetup();
412 attributeWidgetCaches.append( fieldFormatter->createCache( childLayer, fieldIndex, setup.config() ) );
413 fieldIndex++;
414 }
415 QgsFeature relatedFet;
416 while ( it.nextFeature( relatedFet ) )
417 {
418 relatedFeatureAttributes += QgsJsonUtils::exportAttributesToJsonObject( relatedFet, childLayer, attributeWidgetCaches, mUseFieldFormatters );
419 }
420 }
421 properties[relation.name().toStdString()] = relatedFeatureAttributes;
422 }
423 }
424 }
425 featureJson["properties"] = properties;
426
427 return featureJson;
428}
429
430QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features, int indent ) const
431{
432 return QString::fromStdString( exportFeaturesToJsonObject( features ).dump( indent ) );
433}
434
436{
437 json data { { "type", "FeatureCollection" }, { "features", json::array() } };
438 const bool omitCollectionLevelInformation = mOmitCollectionLevelInformation;
439 mOmitCollectionLevelInformation = true;
440 switch ( mGeoJsonProfile )
441 {
444 {
445 for ( const QgsFeature &feature : features )
446 {
447 data["features"].push_back( exportFeatureToJsonObject( feature ) );
448 }
449 break;
450 }
453 {
454 json conformsTo = json::array( { { "http://www.opengis.net/spec/json-fg-1/1.0/conf/core" } } );
455 QgsJsonUtils::addCrsInfo( data, mDestinationCrs, mGeoJsonProfile );
456 bool hasCircularArcs = false;
457 bool hasMeasure = false;
458 for ( const QgsFeature &feature : features )
459 {
460 const QgsGeometry geom = feature.geometry();
461 const Qgis::WkbType flatType = QgsWkbTypes::flatType( geom.wkbType() );
462 if ( flatType == Qgis::WkbType::CircularString || flatType == Qgis::WkbType::CompoundCurve || flatType == Qgis::WkbType::CurvePolygon )
463 {
464 hasCircularArcs = true;
465 }
466 if ( QgsWkbTypes::hasM( feature.geometry().wkbType() ) )
467 {
468 hasMeasure = true;
469 }
470 data["features"].push_back( exportFeatureToJsonObject( feature ) );
471 }
472 if ( hasCircularArcs )
473 {
474 conformsTo.push_back( { "http://www.opengis.net/spec/json-fg-1/1.0/conf/circular-arcs" } );
475 }
476 if ( hasMeasure )
477 {
478 conformsTo.push_back( { "http://www.opengis.net/spec/json-fg-1/1.0/conf/measures" } );
479 data["measures"] = { { "enabled", true } };
480 }
481 data["conformsTo"] = conformsTo;
482 break;
483 }
484 }
485 mOmitCollectionLevelInformation = omitCollectionLevelInformation;
486 return data;
487}
488
490{
491 mDestinationCrs = destinationCrs;
492 mTransform.setDestinationCrs( mDestinationCrs );
493}
494
496{
497 return mGeoJsonProfile;
498}
499
501{
502 mGeoJsonProfile = profile;
503}
504
505//
506// QgsJsonUtils
507//
508
509QgsFeatureList QgsJsonUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
510{
511 if ( !encoding )
512 encoding = QTextCodec::codecForName( "UTF-8" );
513
514 return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
515}
516
517QgsFields QgsJsonUtils::stringToFields( const QString &string, QTextCodec *encoding )
518{
519 if ( !encoding )
520 encoding = QTextCodec::codecForName( "UTF-8" );
521
522 return QgsOgrUtils::stringToFields( string, encoding );
523}
524
525QString QgsJsonUtils::encodeValue( const QVariant &value )
526{
527 if ( QgsVariantUtils::isNull( value ) )
528 return u"null"_s;
529
530 switch ( value.userType() )
531 {
532 case QMetaType::Type::Int:
533 case QMetaType::Type::UInt:
534 case QMetaType::Type::LongLong:
535 case QMetaType::Type::ULongLong:
536 case QMetaType::Type::Double:
537 return value.toString();
538
539 case QMetaType::Type::Bool:
540 return value.toBool() ? "true" : "false";
541
542 case QMetaType::Type::QStringList:
543 case QMetaType::Type::QVariantList:
544 case QMetaType::Type::QVariantMap:
545 return QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson( QJsonDocument::Compact ) );
546
547 default:
548 case QMetaType::Type::QString:
549 QString v
550 = value.toString().replace( '\\', "\\\\"_L1 ).replace( '"', "\\\""_L1 ).replace( '\r', "\\r"_L1 ).replace( '\b', "\\b"_L1 ).replace( '\t', "\\t"_L1 ).replace( '/', "\\/"_L1 ).replace( '\n', "\\n"_L1 );
551
552 return v.prepend( '"' ).append( '"' );
553 }
554}
555
556QString QgsJsonUtils::exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
557{
558 QgsFields fields = feature.fields();
559 QString attrs;
560 for ( int i = 0; i < fields.count(); ++i )
561 {
562 if ( i > 0 )
563 attrs += ",\n"_L1;
564
565 QVariant val = feature.attributes().at( i );
566
567 if ( layer )
568 {
569 QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
571 if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
572 val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
573 }
574
575 attrs += encodeValue( fields.at( i ).name() ) + ':' + encodeValue( val );
576 }
577 return attrs.prepend( '{' ).append( '}' );
578}
579
580QVariantList QgsJsonUtils::parseArray( const QString &json, QMetaType::Type type )
581{
582 QString errorMessage;
583 QVariantList result;
584 try
585 {
586 const auto jObj( json::parse( json.toStdString() ) );
587 if ( !jObj.is_array() )
588 {
589 throw json::parse_error::create( 0, 0, u"JSON value must be an array"_s.toStdString(), &jObj );
590 }
591 for ( const auto &item : jObj )
592 {
593 // Create a QVariant from the array item
594 QVariant v;
595 if ( item.is_number_integer() )
596 {
597 v = item.get<int>();
598 }
599 else if ( item.is_number_unsigned() )
600 {
601 v = item.get<unsigned>();
602 }
603 else if ( item.is_number_float() )
604 {
605 // Note: it's a double and not a float on purpose
606 v = item.get<double>();
607 }
608 else if ( item.is_string() )
609 {
610 v = QString::fromStdString( item.get<std::string>() );
611 }
612 else if ( item.is_boolean() )
613 {
614 v = item.get<bool>();
615 }
616 else if ( item.is_null() )
617 {
618 // Fallback to int
619 v = QgsVariantUtils::createNullVariant( type == QMetaType::Type::UnknownType ? QMetaType::Type::Int : type );
620 }
621
622 // If a destination type was specified (it's not invalid), try to convert
623 if ( type != QMetaType::Type::UnknownType )
624 {
625 if ( !v.convert( static_cast<int>( type ) ) )
626 {
627 QgsLogger::warning( u"Cannot convert json array element to specified type, ignoring: %1"_s.arg( v.toString() ) );
628 }
629 else
630 {
631 result.push_back( v );
632 }
633 }
634 else
635 {
636 result.push_back( v );
637 }
638 }
639 }
640 catch ( json::parse_error &ex )
641 {
642 errorMessage = ex.what();
643 QgsLogger::warning( u"Cannot parse json (%1): %2"_s.arg( ex.what(), json ) );
644 }
645
646 return result;
647}
648
649QVariantList QgsJsonUtils::parseArray( const QString &json, QVariant::Type type )
650{
652}
653
654std::unique_ptr< QgsPoint> parsePointFromGeoJson( const json &coords, bool hasM = false )
655{
656 if ( !coords.is_array() || coords.size() < 2 || coords.size() > 4 )
657 {
658 QgsDebugError( u"JSON Point geometry coordinates must be an array of two, three or four numbers"_s );
659 return nullptr;
660 }
661
662 const double x = coords[0].get< double >();
663 const double y = coords[1].get< double >();
664 if ( coords.size() == 2 )
665 {
666 return std::make_unique< QgsPoint >( x, y );
667 }
668 else if ( coords.size() == 3 )
669 {
670 const double zOrM = coords[2].get< double >();
671 if ( hasM )
672 return std::make_unique< QgsPoint >( x, y, std::numeric_limits<double>::quiet_NaN(), zOrM );
673 else
674 return std::make_unique< QgsPoint >( x, y, zOrM );
675 }
676 else
677 {
678 const double z = coords[2].get< double >();
679 const double m = coords[3].get< double >();
680 return std::make_unique< QgsPoint >( x, y, z, m );
681 }
682}
683
684std::unique_ptr< QgsLineString> parseLineStringFromGeoJson( const json &coords, bool hasM = false )
685{
686 if ( !coords.is_array() )
687 {
688 QgsDebugError( u"JSON LineString geometry coordinates must be an array"_s );
689 return nullptr;
690 }
691
692 const std::size_t coordsSize = coords.size();
693
694 if ( coordsSize == 0 )
695 {
696 // Empty LineString is valid, return an empty geometry
697 return std::make_unique< QgsLineString >();
698 }
699
700 if ( coordsSize < 2 )
701 {
702 QgsDebugError( u"JSON LineString geometry coordinates must contain at least two positions"_s );
703 return nullptr;
704 }
705
706 QVector< double > x;
707 QVector< double > y;
708 QVector< double > z;
709 QVector< double > m;
710 x.resize( coordsSize );
711 y.resize( coordsSize );
712 z.resize( coordsSize );
713 m.resize( coordsSize );
714
715 double *xOut = x.data();
716 double *yOut = y.data();
717 double *zOut = z.data();
718 double *mOut = m.data();
719 bool hasZ = false;
720 for ( const auto &coord : coords )
721 {
722 if ( !coord.is_array() || coord.size() < 2 || coord.size() > 4 )
723 {
724 QgsDebugError( u"JSON LineString geometry coordinates must be an array of two, three or four numbers"_s );
725 return nullptr;
726 }
727
728 *xOut++ = coord[0].get< double >();
729 *yOut++ = coord[1].get< double >();
730 if ( coord.size() == 4 )
731 {
732 *zOut++ = coord[2].get< double >();
733 *mOut++ = coord[3].get< double >();
734 }
735 else if ( coord.size() == 3 )
736 {
737 if ( hasM )
738 {
739 *mOut++ = coord[2].get< double >();
740 *zOut++ = std::numeric_limits< double >::quiet_NaN();
741 }
742 else
743 {
744 *zOut++ = coord[2].get< double >();
745 *mOut++ = std::numeric_limits< double >::quiet_NaN();
746 hasZ = true;
747 }
748 }
749 else
750 {
751 *zOut++ = std::numeric_limits< double >::quiet_NaN();
752 }
753 }
754
755 return std::make_unique< QgsLineString >( x, y, hasZ ? z : QVector<double>(), hasM ? m : QVector<double>() );
756}
757
758std::unique_ptr< QgsPolygon > parsePolygonFromGeoJson( const json &coords, bool hasM = false )
759{
760 if ( !coords.is_array() )
761 {
762 QgsDebugError( u"JSON Polygon geometry coordinates must be an array"_s );
763 return nullptr;
764 }
765
766 if ( coords.empty() )
767 {
768 // Empty polygon
769 return std::make_unique< QgsPolygon >();
770 }
771
772 const std::size_t coordsSize = coords.size();
773 std::unique_ptr< QgsLineString > exterior = parseLineStringFromGeoJson( coords[0], hasM );
774 if ( !exterior )
775 {
776 return nullptr;
777 }
778
779 auto polygon = std::make_unique< QgsPolygon >( exterior.release() );
780 for ( std::size_t i = 1; i < coordsSize; ++i )
781 {
782 std::unique_ptr< QgsLineString > ring = parseLineStringFromGeoJson( coords[i], hasM );
783 if ( !ring )
784 {
785 return nullptr;
786 }
787 polygon->addInteriorRing( ring.release() );
788 }
789 return polygon;
790}
791
792std::unique_ptr< QgsAbstractGeometry > parseGeometryFromGeoJson( const json &geometry, bool hasMIn = false )
793{
794 const auto hasM = hasMIn || ( geometry.contains( "measures" ) && geometry["measures"].contains( "enabled" ) && geometry["measures"]["enabled"].get<bool>() );
795
796 if ( !geometry.is_object() )
797 {
798 QgsDebugError( u"JSON geometry value must be an object"_s );
799 return nullptr;
800 }
801
802 if ( !geometry.contains( "type" ) )
803 {
804 QgsDebugError( u"JSON geometry must contain 'type'"_s );
805 return nullptr;
806 }
807
808 const QString type = QString::fromStdString( geometry["type"].get< std::string >() );
809 if ( type.compare( "Point"_L1, Qt::CaseInsensitive ) == 0 )
810 {
811 if ( !geometry.contains( "coordinates" ) )
812 {
813 QgsDebugError( u"JSON Point geometry must contain 'coordinates'"_s );
814 return nullptr;
815 }
816
817 const json &coords = geometry["coordinates"];
818 return parsePointFromGeoJson( coords, hasM );
819 }
820 else if ( type.compare( "MultiPoint"_L1, Qt::CaseInsensitive ) == 0 )
821 {
822 if ( !geometry.contains( "coordinates" ) )
823 {
824 QgsDebugError( u"JSON MultiPoint geometry must contain 'coordinates'"_s );
825 return nullptr;
826 }
827
828 const json &coords = geometry["coordinates"];
829
830 if ( !coords.is_array() )
831 {
832 QgsDebugError( u"JSON MultiPoint geometry coordinates must be an array"_s );
833 return nullptr;
834 }
835
836 auto multiPoint = std::make_unique< QgsMultiPoint >();
837 multiPoint->reserve( static_cast< int >( coords.size() ) );
838 for ( const auto &pointCoords : coords )
839 {
840 std::unique_ptr< QgsPoint > point = parsePointFromGeoJson( pointCoords, hasM );
841 if ( !point )
842 {
843 return nullptr;
844 }
845 multiPoint->addGeometry( point.release() );
846 }
847
848 return multiPoint;
849 }
850 else if ( type.compare( "LineString"_L1, Qt::CaseInsensitive ) == 0 )
851 {
852 if ( !geometry.contains( "coordinates" ) )
853 {
854 QgsDebugError( u"JSON LineString geometry must contain 'coordinates'"_s );
855 return nullptr;
856 }
857
858 const json &coords = geometry["coordinates"];
859 return parseLineStringFromGeoJson( coords, hasM );
860 }
861 else if ( type.compare( "MultiLineString"_L1, Qt::CaseInsensitive ) == 0 )
862 {
863 if ( !geometry.contains( "coordinates" ) )
864 {
865 QgsDebugError( u"JSON MultiLineString geometry must contain 'coordinates'"_s );
866 return nullptr;
867 }
868
869 const json &coords = geometry["coordinates"];
870
871 if ( !coords.is_array() )
872 {
873 QgsDebugError( u"JSON MultiLineString geometry coordinates must be an array"_s );
874 return nullptr;
875 }
876
877 auto multiLineString = std::make_unique< QgsMultiLineString >();
878 multiLineString->reserve( static_cast< int >( coords.size() ) );
879 for ( const auto &lineCoords : coords )
880 {
881 std::unique_ptr< QgsLineString > line = parseLineStringFromGeoJson( lineCoords, hasM );
882 if ( !line )
883 {
884 return nullptr;
885 }
886 multiLineString->addGeometry( line.release() );
887 }
888
889 return multiLineString;
890 }
891 else if ( type.compare( "Polygon"_L1, Qt::CaseInsensitive ) == 0 )
892 {
893 if ( !geometry.contains( "coordinates" ) )
894 {
895 QgsDebugError( u"JSON Polygon geometry must contain 'coordinates'"_s );
896 return nullptr;
897 }
898
899 const json &coords = geometry["coordinates"];
900 if ( !coords.is_array() )
901 {
902 QgsDebugError( u"JSON Polygon geometry coordinates must be an array"_s );
903 return nullptr;
904 }
905
906 return parsePolygonFromGeoJson( coords );
907 }
908 else if ( type.compare( "MultiPolygon"_L1, Qt::CaseInsensitive ) == 0 )
909 {
910 if ( !geometry.contains( "coordinates" ) )
911 {
912 QgsDebugError( u"JSON MultiPolygon geometry must contain 'coordinates'"_s );
913 return nullptr;
914 }
915
916 const json &coords = geometry["coordinates"];
917
918 if ( !coords.is_array() )
919 {
920 QgsDebugError( u"JSON MultiPolygon geometry coordinates must be an array"_s );
921 return nullptr;
922 }
923
924 auto multiPolygon = std::make_unique< QgsMultiPolygon >();
925 multiPolygon->reserve( static_cast< int >( coords.size() ) );
926 for ( const auto &polygonCoords : coords )
927 {
928 std::unique_ptr< QgsPolygon > polygon = parsePolygonFromGeoJson( polygonCoords );
929 if ( !polygon )
930 {
931 return nullptr;
932 }
933 multiPolygon->addGeometry( polygon.release() );
934 }
935
936 return multiPolygon;
937 }
938 // //////////////////////////////////////////////////////////////////////////////////////////////
939 // Handle JSON-FG types CircularString, CompoundCurve, CurvePolygon, MultiCurve, or MultiSurface.
940 else if ( type.compare( "CircularString"_L1, Qt::CaseInsensitive ) == 0 )
941 {
942 if ( !geometry.contains( "coordinates" ) )
943 {
944 QgsDebugError( u"JSON CircularString geometry must contain 'coordinates'"_s );
945 return nullptr;
946 }
947
948 const json &coords = geometry["coordinates"];
949
950 if ( coords.empty() )
951 {
952 return std::make_unique< QgsCircularString >();
953 }
954
955 if ( !coords.is_array() || coords.size() % 2 == 0 || coords.size() < 3 )
956 {
957 QgsDebugError( u"JSON CircularString geometry coordinates must be an array of at least 3 coordinates and the total number must be an odd number"_s );
958 return nullptr;
959 }
960
961 const bool hasZ = coords[0].is_array() && ( coords[0].size() > 3 || ( coords[0].size() == 3 && !hasM ) );
962
963 QVector<double> x;
964 x.reserve( coords.size() );
965 QVector<double> y;
966 y.reserve( coords.size() );
967 QVector<double> z;
968 if ( hasZ )
969 z.reserve( coords.size() );
970 QVector<double> m;
971 if ( hasM )
972 m.reserve( coords.size() );
973
974 for ( const auto &pointCoords : coords )
975 {
976 std::unique_ptr< QgsPoint > point = parsePointFromGeoJson( pointCoords, hasM );
977 if ( !point )
978 {
979 QgsDebugError( u"Invalid point in CircularString geometry"_s );
980 return nullptr;
981 }
982 x.append( point->x() );
983 y.append( point->y() );
984 if ( hasZ )
985 z.append( point->z() );
986 if ( hasM )
987 m.append( point->m() );
988 }
989 return std::make_unique< QgsCircularString >( x, y, z, m );
990 }
991 else if ( type.compare( "CompoundCurve"_L1, Qt::CaseInsensitive ) == 0 )
992 {
993 if ( !geometry.contains( "geometries" ) )
994 {
995 QgsDebugError( u"JSON CompoundCurve geometry must contain 'geometries'"_s );
996 return nullptr;
997 }
998 const json &geometries = geometry["geometries"];
999 if ( !geometries.is_array() )
1000 {
1001 QgsDebugError( u"JSON CompoundCurve geometry geometries must be an array"_s );
1002 return nullptr;
1003 }
1004 auto compoundCurve = std::make_unique< QgsCompoundCurve >();
1005 for ( const auto &geometry : geometries )
1006 {
1007 std::unique_ptr< QgsAbstractGeometry > object = parseGeometryFromGeoJson( geometry, hasM );
1008 if ( !object )
1009 {
1010 return nullptr;
1011 }
1012 const Qgis::WkbType flatType = QgsWkbTypes::flatType( object->wkbType() );
1013 if ( flatType != Qgis::WkbType::LineString && flatType != Qgis::WkbType::CircularString )
1014 {
1015 QgsDebugError( u"JSON CompoundCurve geometries must be of type LineString or CircularString"_s );
1016 return nullptr;
1017 }
1018 compoundCurve->addCurve( static_cast< QgsCurve * >( object.release() ) );
1019 }
1020 return compoundCurve;
1021 }
1022 else if ( type.compare( "CurvePolygon"_L1, Qt::CaseInsensitive ) == 0 )
1023 {
1024 if ( !geometry.contains( "geometries" ) )
1025 {
1026 QgsDebugError( u"JSON CurvePolygon geometry must contain 'geometries'"_s );
1027 return nullptr;
1028 }
1029 const json &geometries = geometry["geometries"];
1030 if ( !geometries.is_array() )
1031 {
1032 QgsDebugError( u"JSON CurvePolygon geometries must be an array"_s );
1033 return nullptr;
1034 }
1035 auto curvePolygon = std::make_unique< QgsCurvePolygon>();
1036 bool isExterior = true;
1037 for ( const auto &geometry : geometries )
1038 {
1039 std::unique_ptr< QgsAbstractGeometry > object = parseGeometryFromGeoJson( geometry, hasM );
1040 if ( !object )
1041 {
1042 return nullptr;
1043 }
1044 const Qgis::WkbType flatType = QgsWkbTypes::flatType( object->wkbType() );
1045 if ( flatType != Qgis::WkbType::LineString && flatType != Qgis::WkbType::CircularString && flatType != Qgis::WkbType::CompoundCurve )
1046 {
1047 QgsDebugError( u"JSON CurvePolygon geometries must be of type LineString, CircularString or CompoundCurve"_s );
1048 return nullptr;
1049 }
1050 if ( isExterior )
1051 {
1052 curvePolygon->setExteriorRing( static_cast< QgsCurve * >( object.release() ) );
1053 isExterior = false;
1054 }
1055 else
1056 {
1057 curvePolygon->addInteriorRing( static_cast< QgsCurve * >( object.release() ) );
1058 }
1059 }
1060 return curvePolygon;
1061 }
1062 else if ( type.compare( "MultiCurve"_L1, Qt::CaseInsensitive ) == 0 )
1063 {
1064 if ( !geometry.contains( "geometries" ) )
1065 {
1066 QgsDebugError( u"JSON MultiCurve geometry must contain 'geometries'"_s );
1067 return nullptr;
1068 }
1069 const json &geometries = geometry["geometries"];
1070 if ( !geometries.is_array() )
1071 {
1072 QgsDebugError( u"JSON MultiCurve geometries must be an array"_s );
1073 return nullptr;
1074 }
1075 auto multiCurve = std::make_unique< QgsMultiCurve>();
1076 for ( const auto &geometry : geometries )
1077 {
1078 std::unique_ptr< QgsAbstractGeometry > object = parseGeometryFromGeoJson( geometry, hasM );
1079 if ( !object )
1080 {
1081 return nullptr;
1082 }
1083 const Qgis::WkbType flatType = QgsWkbTypes::flatType( object->wkbType() );
1084 if ( flatType != Qgis::WkbType::LineString && flatType != Qgis::WkbType::CircularString && flatType != Qgis::WkbType::CompoundCurve )
1085 {
1086 QgsDebugError( u"JSON MultiCurve geometries must be of type LineString, CircularString or CompoundCurve"_s );
1087 return nullptr;
1088 }
1089 multiCurve->addGeometry( static_cast< QgsCurve * >( object.release() ) );
1090 }
1091 return multiCurve;
1092 }
1093 else if ( type.compare( "MultiSurface"_L1, Qt::CaseInsensitive ) == 0 )
1094 {
1095 if ( !geometry.contains( "geometries" ) )
1096 {
1097 QgsDebugError( u"JSON MultiSurface geometry must contain 'geometries'"_s );
1098 return nullptr;
1099 }
1100 const json &geometries = geometry["geometries"];
1101 if ( !geometries.is_array() )
1102 {
1103 QgsDebugError( u"JSON MultiSurface geometries must be an array"_s );
1104 return nullptr;
1105 }
1106 auto multiSurface = std::make_unique< QgsMultiSurface >();
1107 for ( const auto &geometry : geometries )
1108 {
1109 std::unique_ptr< QgsAbstractGeometry > object = parseGeometryFromGeoJson( geometry, hasM );
1110 if ( !object )
1111 {
1112 return nullptr;
1113 }
1114 const Qgis::WkbType flatType = QgsWkbTypes::flatType( object->wkbType() );
1115 if ( flatType != Qgis::WkbType::Polygon && flatType != Qgis::WkbType::CurvePolygon )
1116 {
1117 QgsDebugError( u"JSON MultiSurface geometries must be of type Polygon or CurvePolygon"_s );
1118 return nullptr;
1119 }
1120 multiSurface->addGeometry( static_cast< QgsSurface * >( object.release() ) );
1121 }
1122 return multiSurface;
1123 }
1124 else if ( type.compare( "GeometryCollection"_L1, Qt::CaseInsensitive ) == 0 )
1125 {
1126 if ( !geometry.contains( "geometries" ) )
1127 {
1128 QgsDebugError( u"JSON GeometryCollection geometry must contain 'geometries'"_s );
1129 return nullptr;
1130 }
1131
1132 const json &geometries = geometry["geometries"];
1133
1134 if ( !geometries.is_array() )
1135 {
1136 QgsDebugError( u"JSON GeometryCollection geometries must be an array"_s );
1137 return nullptr;
1138 }
1139
1140 auto collection = std::make_unique< QgsGeometryCollection >();
1141 collection->reserve( static_cast< int >( geometries.size() ) );
1142 for ( const auto &geometry : geometries )
1143 {
1144 std::unique_ptr< QgsAbstractGeometry > object = parseGeometryFromGeoJson( geometry, hasM );
1145 if ( !object )
1146 {
1147 return nullptr;
1148 }
1149 collection->addGeometry( object.release() );
1150 }
1151
1152 return collection;
1153 }
1154
1155 QgsDebugError( u"Unhandled GeoJSON geometry type: %1"_s.arg( type ) );
1156 return nullptr;
1157}
1158
1160{
1161 if ( !geometry.is_object() )
1162 {
1163 QgsDebugError( u"JSON geometry value must be an object"_s );
1164 return QgsGeometry();
1165 }
1166
1167 return QgsGeometry( parseGeometryFromGeoJson( geometry ) );
1168}
1169
1171{
1172 try
1173 {
1174 const auto jObj( json::parse( geometry.toStdString() ) );
1175 return geometryFromGeoJson( jObj );
1176 }
1177 catch ( json::parse_error &ex )
1178 {
1179 QgsDebugError( u"Cannot parse json (%1): %2"_s.arg( geometry, ex.what() ) );
1180 return QgsGeometry();
1181 }
1182}
1183
1185{
1186 return jsonToVariant( geometry.asJsonObject() );
1187}
1188
1189json QgsJsonUtils::jsonFromVariant( const QVariant &val )
1190{
1191 if ( QgsVariantUtils::isNull( val ) )
1192 {
1193 return nullptr;
1194 }
1195 json j;
1196 if ( val.userType() == QMetaType::Type::QVariantMap )
1197 {
1198 const QVariantMap &vMap = val.toMap();
1199 json jMap = json::object();
1200 for ( auto it = vMap.constBegin(); it != vMap.constEnd(); it++ )
1201 {
1202 jMap[it.key().toStdString()] = jsonFromVariant( it.value() );
1203 }
1204 j = jMap;
1205 }
1206 else if ( val.userType() == QMetaType::Type::QVariantList || val.userType() == QMetaType::Type::QStringList )
1207 {
1208 const QVariantList &vList = val.toList();
1209 json jList = json::array();
1210 for ( const auto &v : vList )
1211 {
1212 jList.push_back( jsonFromVariant( v ) );
1213 }
1214 j = jList;
1215 }
1216 else
1217 {
1218 switch ( val.userType() )
1219 {
1220 case QMetaType::Int:
1221 case QMetaType::UInt:
1222 case QMetaType::LongLong:
1223 case QMetaType::ULongLong:
1224 j = val.toLongLong();
1225 break;
1226 case QMetaType::Double:
1227 case QMetaType::Float:
1228 j = val.toDouble();
1229 break;
1230 case QMetaType::Bool:
1231 j = val.toBool();
1232 break;
1233 case QMetaType::QByteArray:
1234 j = val.toByteArray().toBase64().toStdString();
1235 break;
1236 default:
1237 j = val.toString().toStdString();
1238 break;
1239 }
1240 }
1241 return j;
1242}
1243
1244QVariant QgsJsonUtils::parseJson( const std::string &jsonString )
1245{
1246 QString error;
1247 const QVariant res = parseJson( jsonString, error );
1248
1249 if ( !error.isEmpty() )
1250 {
1251 QgsLogger::warning( u"Cannot parse json (%1): %2"_s.arg( error, QString::fromStdString( jsonString ) ) );
1252 }
1253 return res;
1254}
1255
1256QVariant QgsJsonUtils::parseJson( const std::string &jsonString, QString &error )
1257{
1258 error.clear();
1259 try
1260 {
1261 const json j = json::parse( jsonString );
1262 return jsonToVariant( j );
1263 }
1264 catch ( json::parse_error &ex )
1265 {
1266 error = QString::fromStdString( ex.what() );
1267 }
1268 catch ( json::type_error &ex )
1269 {
1270 error = QString::fromStdString( ex.what() );
1271 }
1272 return QVariant();
1273}
1274
1275QVariant QgsJsonUtils::jsonToVariant( const json &value )
1276{
1277 // tracks whether entire json string is a primitive
1278 bool isPrimitive = true;
1279
1280 std::function<QVariant( json )> _parser { [&]( json jObj ) -> QVariant {
1281 QVariant result;
1282 if ( jObj.is_array() )
1283 {
1284 isPrimitive = false;
1285 QVariantList results;
1286 results.reserve( jObj.size() );
1287 for ( const auto &item : jObj )
1288 {
1289 results.push_back( _parser( item ) );
1290 }
1291 result = results;
1292 }
1293 else if ( jObj.is_object() )
1294 {
1295 isPrimitive = false;
1296 QVariantMap results;
1297 for ( const auto &item : jObj.items() )
1298 {
1299 const auto key { QString::fromStdString( item.key() ) };
1300 const auto value { _parser( item.value() ) };
1301 results[key] = value;
1302 }
1303 result = results;
1304 }
1305 else
1306 {
1307 if ( jObj.is_number_unsigned() )
1308 {
1309 // Try signed int and long long first, fall back
1310 // onto unsigned long long
1311 const qulonglong num { jObj.get<qulonglong>() };
1312 if ( num <= std::numeric_limits<int>::max() )
1313 {
1314 result = static_cast<int>( num );
1315 }
1316 else if ( num <= std::numeric_limits<qlonglong>::max() )
1317 {
1318 result = static_cast<qlonglong>( num );
1319 }
1320 else
1321 {
1322 result = num;
1323 }
1324 }
1325 else if ( jObj.is_number_integer() )
1326 {
1327 const qlonglong num { jObj.get<qlonglong>() };
1328 if ( num <= std::numeric_limits<int>::max() && num >= std::numeric_limits<int>::lowest() )
1329 {
1330 result = static_cast<int>( num );
1331 }
1332 else
1333 {
1334 result = num;
1335 }
1336 }
1337 else if ( jObj.is_boolean() )
1338 {
1339 result = jObj.get<bool>();
1340 }
1341 else if ( jObj.is_number_float() )
1342 {
1343 // Note: it's a double and not a float on purpose
1344 result = jObj.get<double>();
1345 }
1346 else if ( jObj.is_string() )
1347 {
1348 if ( isPrimitive && jObj.get<std::string>().length() == 0 )
1349 {
1350 result = QString::fromStdString( jObj.get<std::string>() ).append( "\"" ).insert( 0, "\"" );
1351 }
1352 else
1353 {
1354 result = QString::fromStdString( jObj.get<std::string>() );
1355 }
1356 }
1357 else if ( jObj.is_null() )
1358 {
1359 // Do nothing (leave invalid)
1360 }
1361 }
1362 return result;
1363 } };
1364
1365 return _parser( value );
1366}
1367
1368QVariant QgsJsonUtils::parseJson( const QString &jsonString )
1369{
1370 return jsonString.isEmpty() ? QVariant() : parseJson( jsonString.toStdString() );
1371}
1372
1373json QgsJsonUtils::exportAttributesToJsonObject( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches, bool useFieldFormatters )
1374{
1375 QgsFields fields = feature.fields();
1376 json attrs;
1377 for ( int i = 0; i < fields.count(); ++i )
1378 {
1379 QVariant val = feature.attributes().at( i );
1380
1381 if ( layer && useFieldFormatters )
1382 {
1383 QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
1385 if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
1386 val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
1387 }
1388 attrs[fields.at( i ).name().toStdString()] = jsonFromVariant( val );
1389 }
1390 return attrs;
1391}
1392
1394{
1395 if ( !crs.isValid() )
1396 return;
1397
1398 if ( crs.authid() == "EPSG:4326" || crs.authid() == "CRS:84" || crs.authid() == "OGC:CRS84" )
1399 {
1400 // per spec, default is WGS84, so no need to add anything
1401 return;
1402 }
1403
1404 switch ( profile )
1405 {
1408 {
1409 value["crs"]["type"] = "name";
1410 value["crs"]["properties"]["name"] = crs.toOgcUrn().toStdString();
1411 break;
1412 }
1415 {
1416 value["coordRefSys"] = crs.toOgcUri().toStdString();
1417 break;
1418 }
1419 }
1420}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2380
GeoJsonProfile
GeoJson export Profile according to OGC Features and Geometries JSON - Part 1: Core https://docs....
Definition qgis.h:5134
@ Legacy
Legacy GeoJson profile used in QGIS prior to 4.2, which included some non-standard extensions and dev...
Definition qgis.h:5135
@ Rfc7946
GeoJson profile compliant with RFC7946 standard "http://www.opengis.net/def/profile/OGC/0/rfc7946".
Definition qgis.h:5136
@ JsonFg
GeoJson profile from OGC Features and Geometries JSON Part 1: core "http://www.opengis....
Definition qgis.h:5137
@ JsonFgPlus
GeoJson profile from OGC Features and Geometries JSON Part 1: core "http://www.opengis....
Definition qgis.h:5138
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:314
@ CompoundCurve
CompoundCurve.
Definition qgis.h:325
@ Point
Point.
Definition qgis.h:316
@ LineString
LineString.
Definition qgis.h:317
@ MultiPoint
MultiPoint.
Definition qgis.h:320
@ Polygon
Polygon.
Definition qgis.h:318
@ MultiPolygon
MultiPolygon.
Definition qgis.h:322
@ MultiLineString
MultiLineString.
Definition qgis.h:321
@ CircularString
CircularString.
Definition qgis.h:324
@ GeometryCollection
GeometryCollection.
Definition qgis.h:323
@ CurvePolygon
CurvePolygon.
Definition qgis.h:326
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toOgcUri() const
Returns the crs as OGC URI (format: http://www.opengis.net/def/crs/OGC/1.3/CRS84) Returns an empty st...
QString toOgcUrn() const
Returns the crs as OGC URN (format: urn:ogc:def:crs:OGC:1.3:CRS84) Returns an empty string on failure...
Handles coordinate transforms between two coordinate systems.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
Custom exception class for Coordinate Reference System related exceptions.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
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:754
Container of fields for a vector layer.
Definition qgsfields.h:45
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:49
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, Qgis::GeoJsonProfile profile=Qgis::GeoJsonProfile::Legacy) const
Exports the geometry to a json object with the give precision and following the specified GeoJSON pro...
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
Qgis::GeoJsonProfile geoJsonProfile() const
Returns the GeoJSON profile to use for export.
json exportFeaturesToJsonObject(const QgsFeatureList &features) const
Returns a GeoJSON 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.
void setGeoJsonProfile(Qgis::GeoJsonProfile profile)
Sets the GeoJSON profile to use for export.
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 GeoJson representation of a feature.
static void addCrsInfo(json &value, const QgsCoordinateReferenceSystem &crs, Qgis::GeoJsonProfile profile=Qgis::GeoJsonProfile::Legacy)
Add crs information entry in json object regarding old GeoJSON specification format if it differs fro...
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 QVariant geometryToGeoJsonVariant(const QgsGeometry &geometry)
Converts a geometry to a GeoJSON compatible variant.
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.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
Surface geometry type.
Definition qgssurface.h:34
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 Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
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:7819
QList< QgsFeature > QgsFeatureList
#define FID_IS_NULL(fid)
std::unique_ptr< QgsAbstractGeometry > parseGeometryFromGeoJson(const json &geometry, bool hasMIn=false)
std::unique_ptr< QgsPolygon > parsePolygonFromGeoJson(const json &coords, bool hasM=false)
std::unique_ptr< QgsPoint > parsePointFromGeoJson(const json &coords, bool hasM=false)
std::unique_ptr< QgsLineString > parseLineStringFromGeoJson(const json &coords, bool hasM=false)
#define QgsDebugError(str)
Definition qgslogger.h:71