QGIS API Documentation 4.3.0-Master (59e977eef4b)
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 = QgsProject::instance()->relationManager()->referencedRelations( mLayer.data() ); // skip-keyword-check
390 for ( const auto &relation : std::as_const( relations ) )
391 {
392 QgsFeatureRequest req = relation.getRelatedFeaturesRequest( feature );
394 QgsVectorLayer *childLayer = relation.referencingLayer();
395 json relatedFeatureAttributes;
396 if ( childLayer )
397 {
398 QgsFeatureIterator it = childLayer->getFeatures( req );
399 QVector<QVariant> attributeWidgetCaches;
400 int fieldIndex = 0;
401 const QgsFields fields { childLayer->fields() };
402 for ( const QgsField &field : fields )
403 {
404 QgsEditorWidgetSetup setup = field.editorWidgetSetup();
406 attributeWidgetCaches.append( fieldFormatter->createCache( childLayer, fieldIndex, setup.config() ) );
407 fieldIndex++;
408 }
409 QgsFeature relatedFet;
410 while ( it.nextFeature( relatedFet ) )
411 {
412 relatedFeatureAttributes += QgsJsonUtils::exportAttributesToJsonObject( relatedFet, childLayer, attributeWidgetCaches, mUseFieldFormatters );
413 }
414 }
415 properties[relation.name().toStdString()] = relatedFeatureAttributes;
416 }
417 }
418 }
419 featureJson["properties"] = properties;
420
421 return featureJson;
422}
423
424QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features, int indent ) const
425{
426 return QString::fromStdString( exportFeaturesToJsonObject( features ).dump( indent ) );
427}
428
430{
431 json data { { "type", "FeatureCollection" }, { "features", json::array() } };
432 const bool omitCollectionLevelInformation = mOmitCollectionLevelInformation;
433 mOmitCollectionLevelInformation = true;
434 switch ( mGeoJsonProfile )
435 {
438 {
439 for ( const QgsFeature &feature : features )
440 {
441 data["features"].push_back( exportFeatureToJsonObject( feature ) );
442 }
443 break;
444 }
447 {
448 json conformsTo = json::array( { { "http://www.opengis.net/spec/json-fg-1/1.0/conf/core" } } );
449 QgsJsonUtils::addCrsInfo( data, mDestinationCrs, mGeoJsonProfile );
450 bool hasCircularArcs = false;
451 bool hasMeasure = false;
452 for ( const QgsFeature &feature : features )
453 {
454 const QgsGeometry geom = feature.geometry();
455 const Qgis::WkbType flatType = QgsWkbTypes::flatType( geom.wkbType() );
456 if ( flatType == Qgis::WkbType::CircularString || flatType == Qgis::WkbType::CompoundCurve || flatType == Qgis::WkbType::CurvePolygon )
457 {
458 hasCircularArcs = true;
459 }
460 if ( QgsWkbTypes::hasM( feature.geometry().wkbType() ) )
461 {
462 hasMeasure = true;
463 }
464 data["features"].push_back( exportFeatureToJsonObject( feature ) );
465 }
466 if ( hasCircularArcs )
467 {
468 conformsTo.push_back( { "http://www.opengis.net/spec/json-fg-1/1.0/conf/circular-arcs" } );
469 }
470 if ( hasMeasure )
471 {
472 conformsTo.push_back( { "http://www.opengis.net/spec/json-fg-1/1.0/conf/measures" } );
473 data["measures"] = { { "enabled", true } };
474 }
475 data["conformsTo"] = conformsTo;
476 break;
477 }
478 }
479 mOmitCollectionLevelInformation = omitCollectionLevelInformation;
480 return data;
481}
482
484{
485 mDestinationCrs = destinationCrs;
486 mTransform.setDestinationCrs( mDestinationCrs );
487}
488
490{
491 return mGeoJsonProfile;
492}
493
495{
496 mGeoJsonProfile = profile;
497}
498
499//
500// QgsJsonUtils
501//
502
503QgsFeatureList QgsJsonUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
504{
505 if ( !encoding )
506 encoding = QTextCodec::codecForName( "UTF-8" );
507
508 return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
509}
510
511QgsFields QgsJsonUtils::stringToFields( const QString &string, QTextCodec *encoding )
512{
513 if ( !encoding )
514 encoding = QTextCodec::codecForName( "UTF-8" );
515
516 return QgsOgrUtils::stringToFields( string, encoding );
517}
518
519QString QgsJsonUtils::encodeValue( const QVariant &value )
520{
521 if ( QgsVariantUtils::isNull( value ) )
522 return u"null"_s;
523
524 switch ( value.userType() )
525 {
526 case QMetaType::Type::Int:
527 case QMetaType::Type::UInt:
528 case QMetaType::Type::LongLong:
529 case QMetaType::Type::ULongLong:
530 case QMetaType::Type::Double:
531 return value.toString();
532
533 case QMetaType::Type::Bool:
534 return value.toBool() ? "true" : "false";
535
536 case QMetaType::Type::QStringList:
537 case QMetaType::Type::QVariantList:
538 case QMetaType::Type::QVariantMap:
539 return QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson( QJsonDocument::Compact ) );
540
541 default:
542 case QMetaType::Type::QString:
543 QString v
544 = value.toString().replace( '\\', "\\\\"_L1 ).replace( '"', "\\\""_L1 ).replace( '\r', "\\r"_L1 ).replace( '\b', "\\b"_L1 ).replace( '\t', "\\t"_L1 ).replace( '/', "\\/"_L1 ).replace( '\n', "\\n"_L1 );
545
546 return v.prepend( '"' ).append( '"' );
547 }
548}
549
550QString QgsJsonUtils::exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
551{
552 QgsFields fields = feature.fields();
553 QString attrs;
554 for ( int i = 0; i < fields.count(); ++i )
555 {
556 if ( i > 0 )
557 attrs += ",\n"_L1;
558
559 QVariant val = feature.attributes().at( i );
560
561 if ( layer )
562 {
563 QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
565 if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
566 val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
567 }
568
569 attrs += encodeValue( fields.at( i ).name() ) + ':' + encodeValue( val );
570 }
571 return attrs.prepend( '{' ).append( '}' );
572}
573
574QVariantList QgsJsonUtils::parseArray( const QString &json, QMetaType::Type type )
575{
576 QString errorMessage;
577 QVariantList result;
578 try
579 {
580 const auto jObj( json::parse( json.toStdString() ) );
581 if ( !jObj.is_array() )
582 {
583 throw json::parse_error::create( 0, 0, u"JSON value must be an array"_s.toStdString(), &jObj );
584 }
585 for ( const auto &item : jObj )
586 {
587 // Create a QVariant from the array item
588 QVariant v;
589 if ( item.is_number_integer() )
590 {
591 v = item.get<int>();
592 }
593 else if ( item.is_number_unsigned() )
594 {
595 v = item.get<unsigned>();
596 }
597 else if ( item.is_number_float() )
598 {
599 // Note: it's a double and not a float on purpose
600 v = item.get<double>();
601 }
602 else if ( item.is_string() )
603 {
604 v = QString::fromStdString( item.get<std::string>() );
605 }
606 else if ( item.is_boolean() )
607 {
608 v = item.get<bool>();
609 }
610 else if ( item.is_null() )
611 {
612 // Fallback to int
613 v = QgsVariantUtils::createNullVariant( type == QMetaType::Type::UnknownType ? QMetaType::Type::Int : type );
614 }
615
616 // If a destination type was specified (it's not invalid), try to convert
617 if ( type != QMetaType::Type::UnknownType )
618 {
619 if ( !v.convert( static_cast<int>( type ) ) )
620 {
621 QgsLogger::warning( u"Cannot convert json array element to specified type, ignoring: %1"_s.arg( v.toString() ) );
622 }
623 else
624 {
625 result.push_back( v );
626 }
627 }
628 else
629 {
630 result.push_back( v );
631 }
632 }
633 }
634 catch ( json::parse_error &ex )
635 {
636 errorMessage = ex.what();
637 QgsLogger::warning( u"Cannot parse json (%1): %2"_s.arg( ex.what(), json ) );
638 }
639
640 return result;
641}
642
643QVariantList QgsJsonUtils::parseArray( const QString &json, QVariant::Type type )
644{
646}
647
648std::unique_ptr< QgsPoint> parsePointFromGeoJson( const json &coords, bool hasM = false )
649{
650 if ( !coords.is_array() || coords.size() < 2 || coords.size() > 4 )
651 {
652 QgsDebugError( u"JSON Point geometry coordinates must be an array of two, three or four numbers"_s );
653 return nullptr;
654 }
655
656 const double x = coords[0].get< double >();
657 const double y = coords[1].get< double >();
658 if ( coords.size() == 2 )
659 {
660 return std::make_unique< QgsPoint >( x, y );
661 }
662 else if ( coords.size() == 3 )
663 {
664 const double zOrM = coords[2].get< double >();
665 if ( hasM )
666 return std::make_unique< QgsPoint >( x, y, std::numeric_limits<double>::quiet_NaN(), zOrM );
667 else
668 return std::make_unique< QgsPoint >( x, y, zOrM );
669 }
670 else
671 {
672 const double z = coords[2].get< double >();
673 const double m = coords[3].get< double >();
674 return std::make_unique< QgsPoint >( x, y, z, m );
675 }
676}
677
678std::unique_ptr< QgsLineString> parseLineStringFromGeoJson( const json &coords, bool hasM = false )
679{
680 if ( !coords.is_array() )
681 {
682 QgsDebugError( u"JSON LineString geometry coordinates must be an array"_s );
683 return nullptr;
684 }
685
686 const std::size_t coordsSize = coords.size();
687
688 if ( coordsSize == 0 )
689 {
690 // Empty LineString is valid, return an empty geometry
691 return std::make_unique< QgsLineString >();
692 }
693
694 if ( coordsSize < 2 )
695 {
696 QgsDebugError( u"JSON LineString geometry coordinates must contain at least two positions"_s );
697 return nullptr;
698 }
699
700 QVector< double > x;
701 QVector< double > y;
702 QVector< double > z;
703 QVector< double > m;
704 x.resize( coordsSize );
705 y.resize( coordsSize );
706 z.resize( coordsSize );
707 m.resize( coordsSize );
708
709 double *xOut = x.data();
710 double *yOut = y.data();
711 double *zOut = z.data();
712 double *mOut = m.data();
713 bool hasZ = false;
714 for ( const auto &coord : coords )
715 {
716 if ( !coord.is_array() || coord.size() < 2 || coord.size() > 4 )
717 {
718 QgsDebugError( u"JSON LineString geometry coordinates must be an array of two, three or four numbers"_s );
719 return nullptr;
720 }
721
722 *xOut++ = coord[0].get< double >();
723 *yOut++ = coord[1].get< double >();
724 if ( coord.size() == 4 )
725 {
726 *zOut++ = coord[2].get< double >();
727 *mOut++ = coord[3].get< double >();
728 }
729 else if ( coord.size() == 3 )
730 {
731 if ( hasM )
732 {
733 *mOut++ = coord[2].get< double >();
734 *zOut++ = std::numeric_limits< double >::quiet_NaN();
735 }
736 else
737 {
738 *zOut++ = coord[2].get< double >();
739 *mOut++ = std::numeric_limits< double >::quiet_NaN();
740 hasZ = true;
741 }
742 }
743 else
744 {
745 *zOut++ = std::numeric_limits< double >::quiet_NaN();
746 }
747 }
748
749 return std::make_unique< QgsLineString >( x, y, hasZ ? z : QVector<double>(), hasM ? m : QVector<double>() );
750}
751
752std::unique_ptr< QgsPolygon > parsePolygonFromGeoJson( const json &coords, bool hasM = false )
753{
754 if ( !coords.is_array() )
755 {
756 QgsDebugError( u"JSON Polygon geometry coordinates must be an array"_s );
757 return nullptr;
758 }
759
760 if ( coords.empty() )
761 {
762 // Empty polygon
763 return std::make_unique< QgsPolygon >();
764 }
765
766 const std::size_t coordsSize = coords.size();
767 std::unique_ptr< QgsLineString > exterior = parseLineStringFromGeoJson( coords[0], hasM );
768 if ( !exterior )
769 {
770 return nullptr;
771 }
772
773 auto polygon = std::make_unique< QgsPolygon >( exterior.release() );
774 for ( std::size_t i = 1; i < coordsSize; ++i )
775 {
776 std::unique_ptr< QgsLineString > ring = parseLineStringFromGeoJson( coords[i], hasM );
777 if ( !ring )
778 {
779 return nullptr;
780 }
781 polygon->addInteriorRing( ring.release() );
782 }
783 return polygon;
784}
785
786std::unique_ptr< QgsAbstractGeometry > parseGeometryFromGeoJson( const json &geometry, bool hasMIn = false )
787{
788 const auto hasM = hasMIn || ( geometry.contains( "measures" ) && geometry["measures"].contains( "enabled" ) && geometry["measures"]["enabled"].get<bool>() );
789
790 if ( !geometry.is_object() )
791 {
792 QgsDebugError( u"JSON geometry value must be an object"_s );
793 return nullptr;
794 }
795
796 if ( !geometry.contains( "type" ) )
797 {
798 QgsDebugError( u"JSON geometry must contain 'type'"_s );
799 return nullptr;
800 }
801
802 const QString type = QString::fromStdString( geometry["type"].get< std::string >() );
803 if ( type.compare( "Point"_L1, Qt::CaseInsensitive ) == 0 )
804 {
805 if ( !geometry.contains( "coordinates" ) )
806 {
807 QgsDebugError( u"JSON Point geometry must contain 'coordinates'"_s );
808 return nullptr;
809 }
810
811 const json &coords = geometry["coordinates"];
812 return parsePointFromGeoJson( coords, hasM );
813 }
814 else if ( type.compare( "MultiPoint"_L1, Qt::CaseInsensitive ) == 0 )
815 {
816 if ( !geometry.contains( "coordinates" ) )
817 {
818 QgsDebugError( u"JSON MultiPoint geometry must contain 'coordinates'"_s );
819 return nullptr;
820 }
821
822 const json &coords = geometry["coordinates"];
823
824 if ( !coords.is_array() )
825 {
826 QgsDebugError( u"JSON MultiPoint geometry coordinates must be an array"_s );
827 return nullptr;
828 }
829
830 auto multiPoint = std::make_unique< QgsMultiPoint >();
831 multiPoint->reserve( static_cast< int >( coords.size() ) );
832 for ( const auto &pointCoords : coords )
833 {
834 std::unique_ptr< QgsPoint > point = parsePointFromGeoJson( pointCoords, hasM );
835 if ( !point )
836 {
837 return nullptr;
838 }
839 multiPoint->addGeometry( point.release() );
840 }
841
842 return multiPoint;
843 }
844 else if ( type.compare( "LineString"_L1, Qt::CaseInsensitive ) == 0 )
845 {
846 if ( !geometry.contains( "coordinates" ) )
847 {
848 QgsDebugError( u"JSON LineString geometry must contain 'coordinates'"_s );
849 return nullptr;
850 }
851
852 const json &coords = geometry["coordinates"];
853 return parseLineStringFromGeoJson( coords, hasM );
854 }
855 else if ( type.compare( "MultiLineString"_L1, Qt::CaseInsensitive ) == 0 )
856 {
857 if ( !geometry.contains( "coordinates" ) )
858 {
859 QgsDebugError( u"JSON MultiLineString geometry must contain 'coordinates'"_s );
860 return nullptr;
861 }
862
863 const json &coords = geometry["coordinates"];
864
865 if ( !coords.is_array() )
866 {
867 QgsDebugError( u"JSON MultiLineString geometry coordinates must be an array"_s );
868 return nullptr;
869 }
870
871 auto multiLineString = std::make_unique< QgsMultiLineString >();
872 multiLineString->reserve( static_cast< int >( coords.size() ) );
873 for ( const auto &lineCoords : coords )
874 {
875 std::unique_ptr< QgsLineString > line = parseLineStringFromGeoJson( lineCoords, hasM );
876 if ( !line )
877 {
878 return nullptr;
879 }
880 multiLineString->addGeometry( line.release() );
881 }
882
883 return multiLineString;
884 }
885 else if ( type.compare( "Polygon"_L1, Qt::CaseInsensitive ) == 0 )
886 {
887 if ( !geometry.contains( "coordinates" ) )
888 {
889 QgsDebugError( u"JSON Polygon geometry must contain 'coordinates'"_s );
890 return nullptr;
891 }
892
893 const json &coords = geometry["coordinates"];
894 if ( !coords.is_array() )
895 {
896 QgsDebugError( u"JSON Polygon geometry coordinates must be an array"_s );
897 return nullptr;
898 }
899
900 return parsePolygonFromGeoJson( coords );
901 }
902 else if ( type.compare( "MultiPolygon"_L1, Qt::CaseInsensitive ) == 0 )
903 {
904 if ( !geometry.contains( "coordinates" ) )
905 {
906 QgsDebugError( u"JSON MultiPolygon geometry must contain 'coordinates'"_s );
907 return nullptr;
908 }
909
910 const json &coords = geometry["coordinates"];
911
912 if ( !coords.is_array() )
913 {
914 QgsDebugError( u"JSON MultiPolygon geometry coordinates must be an array"_s );
915 return nullptr;
916 }
917
918 auto multiPolygon = std::make_unique< QgsMultiPolygon >();
919 multiPolygon->reserve( static_cast< int >( coords.size() ) );
920 for ( const auto &polygonCoords : coords )
921 {
922 std::unique_ptr< QgsPolygon > polygon = parsePolygonFromGeoJson( polygonCoords );
923 if ( !polygon )
924 {
925 return nullptr;
926 }
927 multiPolygon->addGeometry( polygon.release() );
928 }
929
930 return multiPolygon;
931 }
932 // //////////////////////////////////////////////////////////////////////////////////////////////
933 // Handle JSON-FG types CircularString, CompoundCurve, CurvePolygon, MultiCurve, or MultiSurface.
934 else if ( type.compare( "CircularString"_L1, Qt::CaseInsensitive ) == 0 )
935 {
936 if ( !geometry.contains( "coordinates" ) )
937 {
938 QgsDebugError( u"JSON CircularString geometry must contain 'coordinates'"_s );
939 return nullptr;
940 }
941
942 const json &coords = geometry["coordinates"];
943
944 if ( coords.empty() )
945 {
946 return std::make_unique< QgsCircularString >();
947 }
948
949 if ( !coords.is_array() || coords.size() % 2 == 0 || coords.size() < 3 )
950 {
951 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 );
952 return nullptr;
953 }
954
955 const bool hasZ = coords[0].is_array() && ( coords[0].size() > 3 || ( coords[0].size() == 3 && !hasM ) );
956
957 QVector<double> x;
958 x.reserve( coords.size() );
959 QVector<double> y;
960 y.reserve( coords.size() );
961 QVector<double> z;
962 if ( hasZ )
963 z.reserve( coords.size() );
964 QVector<double> m;
965 if ( hasM )
966 m.reserve( coords.size() );
967
968 for ( const auto &pointCoords : coords )
969 {
970 std::unique_ptr< QgsPoint > point = parsePointFromGeoJson( pointCoords, hasM );
971 if ( !point )
972 {
973 QgsDebugError( u"Invalid point in CircularString geometry"_s );
974 return nullptr;
975 }
976 x.append( point->x() );
977 y.append( point->y() );
978 if ( hasZ )
979 z.append( point->z() );
980 if ( hasM )
981 m.append( point->m() );
982 }
983 return std::make_unique< QgsCircularString >( x, y, z, m );
984 }
985 else if ( type.compare( "CompoundCurve"_L1, Qt::CaseInsensitive ) == 0 )
986 {
987 if ( !geometry.contains( "geometries" ) )
988 {
989 QgsDebugError( u"JSON CompoundCurve geometry must contain 'geometries'"_s );
990 return nullptr;
991 }
992 const json &geometries = geometry["geometries"];
993 if ( !geometries.is_array() )
994 {
995 QgsDebugError( u"JSON CompoundCurve geometry geometries must be an array"_s );
996 return nullptr;
997 }
998 auto compoundCurve = std::make_unique< QgsCompoundCurve >();
999 for ( const auto &geometry : geometries )
1000 {
1001 std::unique_ptr< QgsAbstractGeometry > object = parseGeometryFromGeoJson( geometry, hasM );
1002 if ( !object )
1003 {
1004 return nullptr;
1005 }
1006 const Qgis::WkbType flatType = QgsWkbTypes::flatType( object->wkbType() );
1007 if ( flatType != Qgis::WkbType::LineString && flatType != Qgis::WkbType::CircularString )
1008 {
1009 QgsDebugError( u"JSON CompoundCurve geometries must be of type LineString or CircularString"_s );
1010 return nullptr;
1011 }
1012 compoundCurve->addCurve( static_cast< QgsCurve * >( object.release() ) );
1013 }
1014 return compoundCurve;
1015 }
1016 else if ( type.compare( "CurvePolygon"_L1, Qt::CaseInsensitive ) == 0 )
1017 {
1018 if ( !geometry.contains( "geometries" ) )
1019 {
1020 QgsDebugError( u"JSON CurvePolygon geometry must contain 'geometries'"_s );
1021 return nullptr;
1022 }
1023 const json &geometries = geometry["geometries"];
1024 if ( !geometries.is_array() )
1025 {
1026 QgsDebugError( u"JSON CurvePolygon geometries must be an array"_s );
1027 return nullptr;
1028 }
1029 auto curvePolygon = std::make_unique< QgsCurvePolygon>();
1030 bool isExterior = true;
1031 for ( const auto &geometry : geometries )
1032 {
1033 std::unique_ptr< QgsAbstractGeometry > object = parseGeometryFromGeoJson( geometry, hasM );
1034 if ( !object )
1035 {
1036 return nullptr;
1037 }
1038 const Qgis::WkbType flatType = QgsWkbTypes::flatType( object->wkbType() );
1039 if ( flatType != Qgis::WkbType::LineString && flatType != Qgis::WkbType::CircularString && flatType != Qgis::WkbType::CompoundCurve )
1040 {
1041 QgsDebugError( u"JSON CurvePolygon geometries must be of type LineString, CircularString or CompoundCurve"_s );
1042 return nullptr;
1043 }
1044 if ( isExterior )
1045 {
1046 curvePolygon->setExteriorRing( static_cast< QgsCurve * >( object.release() ) );
1047 isExterior = false;
1048 }
1049 else
1050 {
1051 curvePolygon->addInteriorRing( static_cast< QgsCurve * >( object.release() ) );
1052 }
1053 }
1054 return curvePolygon;
1055 }
1056 else if ( type.compare( "MultiCurve"_L1, Qt::CaseInsensitive ) == 0 )
1057 {
1058 if ( !geometry.contains( "geometries" ) )
1059 {
1060 QgsDebugError( u"JSON MultiCurve geometry must contain 'geometries'"_s );
1061 return nullptr;
1062 }
1063 const json &geometries = geometry["geometries"];
1064 if ( !geometries.is_array() )
1065 {
1066 QgsDebugError( u"JSON MultiCurve geometries must be an array"_s );
1067 return nullptr;
1068 }
1069 auto multiCurve = std::make_unique< QgsMultiCurve>();
1070 for ( const auto &geometry : geometries )
1071 {
1072 std::unique_ptr< QgsAbstractGeometry > object = parseGeometryFromGeoJson( geometry, hasM );
1073 if ( !object )
1074 {
1075 return nullptr;
1076 }
1077 const Qgis::WkbType flatType = QgsWkbTypes::flatType( object->wkbType() );
1078 if ( flatType != Qgis::WkbType::LineString && flatType != Qgis::WkbType::CircularString && flatType != Qgis::WkbType::CompoundCurve )
1079 {
1080 QgsDebugError( u"JSON MultiCurve geometries must be of type LineString, CircularString or CompoundCurve"_s );
1081 return nullptr;
1082 }
1083 multiCurve->addGeometry( static_cast< QgsCurve * >( object.release() ) );
1084 }
1085 return multiCurve;
1086 }
1087 else if ( type.compare( "MultiSurface"_L1, Qt::CaseInsensitive ) == 0 )
1088 {
1089 if ( !geometry.contains( "geometries" ) )
1090 {
1091 QgsDebugError( u"JSON MultiSurface geometry must contain 'geometries'"_s );
1092 return nullptr;
1093 }
1094 const json &geometries = geometry["geometries"];
1095 if ( !geometries.is_array() )
1096 {
1097 QgsDebugError( u"JSON MultiSurface geometries must be an array"_s );
1098 return nullptr;
1099 }
1100 auto multiSurface = std::make_unique< QgsMultiSurface >();
1101 for ( const auto &geometry : geometries )
1102 {
1103 std::unique_ptr< QgsAbstractGeometry > object = parseGeometryFromGeoJson( geometry, hasM );
1104 if ( !object )
1105 {
1106 return nullptr;
1107 }
1108 const Qgis::WkbType flatType = QgsWkbTypes::flatType( object->wkbType() );
1109 if ( flatType != Qgis::WkbType::Polygon && flatType != Qgis::WkbType::CurvePolygon )
1110 {
1111 QgsDebugError( u"JSON MultiSurface geometries must be of type Polygon or CurvePolygon"_s );
1112 return nullptr;
1113 }
1114 multiSurface->addGeometry( static_cast< QgsSurface * >( object.release() ) );
1115 }
1116 return multiSurface;
1117 }
1118 else if ( type.compare( "GeometryCollection"_L1, Qt::CaseInsensitive ) == 0 )
1119 {
1120 if ( !geometry.contains( "geometries" ) )
1121 {
1122 QgsDebugError( u"JSON GeometryCollection geometry must contain 'geometries'"_s );
1123 return nullptr;
1124 }
1125
1126 const json &geometries = geometry["geometries"];
1127
1128 if ( !geometries.is_array() )
1129 {
1130 QgsDebugError( u"JSON GeometryCollection geometries must be an array"_s );
1131 return nullptr;
1132 }
1133
1134 auto collection = std::make_unique< QgsGeometryCollection >();
1135 collection->reserve( static_cast< int >( geometries.size() ) );
1136 for ( const auto &geometry : geometries )
1137 {
1138 std::unique_ptr< QgsAbstractGeometry > object = parseGeometryFromGeoJson( geometry, hasM );
1139 if ( !object )
1140 {
1141 return nullptr;
1142 }
1143 collection->addGeometry( object.release() );
1144 }
1145
1146 return collection;
1147 }
1148
1149 QgsDebugError( u"Unhandled GeoJSON geometry type: %1"_s.arg( type ) );
1150 return nullptr;
1151}
1152
1154{
1155 if ( !geometry.is_object() )
1156 {
1157 QgsDebugError( u"JSON geometry value must be an object"_s );
1158 return QgsGeometry();
1159 }
1160
1161 return QgsGeometry( parseGeometryFromGeoJson( geometry ) );
1162}
1163
1165{
1166 try
1167 {
1168 const auto jObj( json::parse( geometry.toStdString() ) );
1169 return geometryFromGeoJson( jObj );
1170 }
1171 catch ( json::parse_error &ex )
1172 {
1173 QgsDebugError( u"Cannot parse json (%1): %2"_s.arg( geometry, ex.what() ) );
1174 return QgsGeometry();
1175 }
1176}
1177
1179{
1180 return jsonToVariant( geometry.asJsonObject() );
1181}
1182
1183json QgsJsonUtils::jsonFromVariant( const QVariant &val )
1184{
1185 if ( QgsVariantUtils::isNull( val ) )
1186 {
1187 return nullptr;
1188 }
1189 json j;
1190 if ( val.userType() == QMetaType::Type::QVariantMap )
1191 {
1192 const QVariantMap &vMap = val.toMap();
1193 json jMap = json::object();
1194 for ( auto it = vMap.constBegin(); it != vMap.constEnd(); it++ )
1195 {
1196 jMap[it.key().toStdString()] = jsonFromVariant( it.value() );
1197 }
1198 j = jMap;
1199 }
1200 else if ( val.userType() == QMetaType::Type::QVariantList || val.userType() == QMetaType::Type::QStringList )
1201 {
1202 const QVariantList &vList = val.toList();
1203 json jList = json::array();
1204 for ( const auto &v : vList )
1205 {
1206 jList.push_back( jsonFromVariant( v ) );
1207 }
1208 j = jList;
1209 }
1210 else
1211 {
1212 switch ( val.userType() )
1213 {
1214 case QMetaType::Int:
1215 case QMetaType::UInt:
1216 case QMetaType::LongLong:
1217 case QMetaType::ULongLong:
1218 j = val.toLongLong();
1219 break;
1220 case QMetaType::Double:
1221 case QMetaType::Float:
1222 j = val.toDouble();
1223 break;
1224 case QMetaType::Bool:
1225 j = val.toBool();
1226 break;
1227 case QMetaType::QByteArray:
1228 j = val.toByteArray().toBase64().toStdString();
1229 break;
1230 default:
1231 j = val.toString().toStdString();
1232 break;
1233 }
1234 }
1235 return j;
1236}
1237
1238QVariant QgsJsonUtils::parseJson( const std::string &jsonString )
1239{
1240 QString error;
1241 const QVariant res = parseJson( jsonString, error );
1242
1243 if ( !error.isEmpty() )
1244 {
1245 QgsLogger::warning( u"Cannot parse json (%1): %2"_s.arg( error, QString::fromStdString( jsonString ) ) );
1246 }
1247 return res;
1248}
1249
1250QVariant QgsJsonUtils::parseJson( const std::string &jsonString, QString &error )
1251{
1252 error.clear();
1253 try
1254 {
1255 const json j = json::parse( jsonString );
1256 return jsonToVariant( j );
1257 }
1258 catch ( json::parse_error &ex )
1259 {
1260 error = QString::fromStdString( ex.what() );
1261 }
1262 catch ( json::type_error &ex )
1263 {
1264 error = QString::fromStdString( ex.what() );
1265 }
1266 return QVariant();
1267}
1268
1269QVariant QgsJsonUtils::jsonToVariant( const json &value )
1270{
1271 // tracks whether entire json string is a primitive
1272 bool isPrimitive = true;
1273
1274 std::function<QVariant( json )> _parser { [&]( json jObj ) -> QVariant {
1275 QVariant result;
1276 if ( jObj.is_array() )
1277 {
1278 isPrimitive = false;
1279 QVariantList results;
1280 results.reserve( jObj.size() );
1281 for ( const auto &item : jObj )
1282 {
1283 results.push_back( _parser( item ) );
1284 }
1285 result = results;
1286 }
1287 else if ( jObj.is_object() )
1288 {
1289 isPrimitive = false;
1290 QVariantMap results;
1291 for ( const auto &item : jObj.items() )
1292 {
1293 const auto key { QString::fromStdString( item.key() ) };
1294 const auto value { _parser( item.value() ) };
1295 results[key] = value;
1296 }
1297 result = results;
1298 }
1299 else
1300 {
1301 if ( jObj.is_number_unsigned() )
1302 {
1303 // Try signed int and long long first, fall back
1304 // onto unsigned long long
1305 const qulonglong num { jObj.get<qulonglong>() };
1306 if ( num <= std::numeric_limits<int>::max() )
1307 {
1308 result = static_cast<int>( num );
1309 }
1310 else if ( num <= std::numeric_limits<qlonglong>::max() )
1311 {
1312 result = static_cast<qlonglong>( num );
1313 }
1314 else
1315 {
1316 result = num;
1317 }
1318 }
1319 else if ( jObj.is_number_integer() )
1320 {
1321 const qlonglong num { jObj.get<qlonglong>() };
1322 if ( num <= std::numeric_limits<int>::max() && num >= std::numeric_limits<int>::lowest() )
1323 {
1324 result = static_cast<int>( num );
1325 }
1326 else
1327 {
1328 result = num;
1329 }
1330 }
1331 else if ( jObj.is_boolean() )
1332 {
1333 result = jObj.get<bool>();
1334 }
1335 else if ( jObj.is_number_float() )
1336 {
1337 // Note: it's a double and not a float on purpose
1338 result = jObj.get<double>();
1339 }
1340 else if ( jObj.is_string() )
1341 {
1342 if ( isPrimitive && jObj.get<std::string>().length() == 0 )
1343 {
1344 result = QString::fromStdString( jObj.get<std::string>() ).append( "\"" ).insert( 0, "\"" );
1345 }
1346 else
1347 {
1348 result = QString::fromStdString( jObj.get<std::string>() );
1349 }
1350 }
1351 else if ( jObj.is_null() )
1352 {
1353 // Do nothing (leave invalid)
1354 }
1355 }
1356 return result;
1357 } };
1358
1359 return _parser( value );
1360}
1361
1362QVariant QgsJsonUtils::parseJson( const QString &jsonString )
1363{
1364 return jsonString.isEmpty() ? QVariant() : parseJson( jsonString.toStdString() );
1365}
1366
1367json QgsJsonUtils::exportAttributesToJsonObject( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches, bool useFieldFormatters )
1368{
1369 QgsFields fields = feature.fields();
1370 json attrs;
1371 for ( int i = 0; i < fields.count(); ++i )
1372 {
1373 QVariant val = feature.attributes().at( i );
1374
1375 if ( layer && useFieldFormatters )
1376 {
1377 QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
1379 if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
1380 val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
1381 }
1382 attrs[fields.at( i ).name().toStdString()] = jsonFromVariant( val );
1383 }
1384 return attrs;
1385}
1386
1388{
1389 if ( !crs.isValid() )
1390 return;
1391
1392 if ( crs.authid() == "EPSG:4326" || crs.authid() == "CRS:84" || crs.authid() == "OGC:CRS84" )
1393 {
1394 // per spec, default is WGS84, so no need to add anything
1395 return;
1396 }
1397
1398 switch ( profile )
1399 {
1402 {
1403 value["crs"]["type"] = "name";
1404 value["crs"]["properties"]["name"] = crs.toOgcUrn().toStdString();
1405 break;
1406 }
1409 {
1410 value["coordRefSys"] = crs.toOgcUri().toStdString();
1411 break;
1412 }
1413 }
1414}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2359
GeoJsonProfile
GeoJson export Profile according to OGC Features and Geometries JSON - Part 1: Core https://docs....
Definition qgis.h:5061
@ Legacy
Legacy GeoJson profile used in QGIS prior to 4.2, which included some non-standard extensions and dev...
Definition qgis.h:5062
@ Rfc7946
GeoJson profile compliant with RFC7946 standard "http://www.opengis.net/def/profile/OGC/0/rfc7946".
Definition qgis.h:5063
@ JsonFg
GeoJson profile from OGC Features and Geometries JSON Part 1: core "http://www.opengis....
Definition qgis.h:5064
@ JsonFgPlus
GeoJson profile from OGC Features and Geometries JSON Part 1: core "http://www.opengis....
Definition qgis.h:5065
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ CompoundCurve
CompoundCurve.
Definition qgis.h:305
@ Point
Point.
Definition qgis.h:296
@ LineString
LineString.
Definition qgis.h:297
@ MultiPoint
MultiPoint.
Definition qgis.h:300
@ Polygon
Polygon.
Definition qgis.h:298
@ MultiPolygon
MultiPolygon.
Definition qgis.h:302
@ MultiLineString
MultiLineString.
Definition qgis.h:301
@ CircularString
CircularString.
Definition qgis.h:304
@ GeometryCollection
GeometryCollection.
Definition qgis.h:303
@ CurvePolygon
CurvePolygon.
Definition qgis.h:306
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: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, 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.
QgsRelationManager * relationManager
Definition qgsproject.h:125
static QgsProject * instance()
Returns the QgsProject singleton instance.
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
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:7511
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