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