QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
qgsjsonutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsjsonutils.h
3 -------------
4 Date : May 206
5 Copyright : (C) 2016 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsjsonutils.h"
17#include "qgsfeatureiterator.h"
18#include "qgsogrutils.h"
19#include "qgsgeometry.h"
20#include "qgsvectorlayer.h"
21#include "qgsrelation.h"
22#include "qgsrelationmanager.h"
23#include "qgsproject.h"
24#include "qgsexception.h"
25#include "qgslogger.h"
27#include "qgsfieldformatter.h"
28#include "qgsapplication.h"
29#include "qgsfeatureid.h"
30
31#include <QJsonDocument>
32#include <QJsonArray>
33#include <QTextCodec>
34#include <nlohmann/json.hpp>
35
37 : mPrecision( precision )
38 , mLayer( vectorLayer )
39{
40 if ( vectorLayer )
41 {
42 mCrs = vectorLayer->crs();
43 mTransform.setSourceCrs( mCrs );
44 }
45
46 // Default 4326
47 mDestinationCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );
48 mTransform.setDestinationCrs( mDestinationCrs );
49}
50
52{
53 mLayer = vectorLayer;
54 if ( vectorLayer )
55 {
56 mCrs = vectorLayer->crs();
57 mTransform.setSourceCrs( mCrs );
58 }
59}
60
62{
63 return mLayer.data();
64}
65
67{
68 mCrs = crs;
69 mTransform.setSourceCrs( mCrs );
70}
71
76
77QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties,
78 const QVariant &id, int indent ) const
79{
80 return QString::fromStdString( exportFeatureToJsonObject( feature, extraProperties, id ).dump( indent ) );
81}
82
83json QgsJsonExporter::exportFeatureToJsonObject( const QgsFeature &feature, const QVariantMap &extraProperties, const QVariant &id ) const
84{
85 json featureJson
86 {
87 { "type", "Feature" },
88 };
89 if ( id.isValid() )
90 {
91 bool ok = false;
92 auto intId = id.toLongLong( &ok );
93 if ( ok )
94 {
95 featureJson["id"] = intId;
96 }
97 else
98 {
99 featureJson["id"] = id.toString().toStdString();
100 }
101 }
102 else if ( FID_IS_NULL( feature.id() ) )
103 {
104 featureJson["id"] = nullptr;
105 }
106 else
107 {
108 featureJson["id"] = feature.id();
109 }
110
111 QgsGeometry geom = feature.geometry();
112 if ( !geom.isNull() && mIncludeGeometry )
113 {
114 if ( mCrs.isValid() )
115 {
116 try
117 {
118 QgsGeometry transformed = geom;
119 if ( mTransformGeometries && transformed.transform( mTransform ) == Qgis::GeometryOperationResult::Success )
120 geom = transformed;
121 }
122 catch ( QgsCsException &cse )
123 {
124 Q_UNUSED( cse )
125 }
126 }
127 QgsRectangle box = geom.boundingBox();
128
130 {
131 featureJson[ "bbox" ] =
132 {
133 qgsRound( box.xMinimum(), mPrecision ),
134 qgsRound( box.yMinimum(), mPrecision ),
135 qgsRound( box.xMaximum(), mPrecision ),
136 qgsRound( box.yMaximum(), mPrecision )
137 };
138 }
139 featureJson[ "geometry" ] = geom.asJsonObject( mPrecision );
140 }
141 else
142 {
143 featureJson[ "geometry" ] = nullptr;
144 }
145
146 // build up properties element
147 json properties;
148 if ( mIncludeAttributes || !extraProperties.isEmpty() )
149 {
150 //read all attribute values from the feature
151 if ( mIncludeAttributes )
152 {
153 QgsFields fields = mLayer ? mLayer->fields() : feature.fields();
154 // List of formatters through we want to pass the values
155 QStringList formattersAllowList;
156 formattersAllowList << QStringLiteral( "KeyValue" )
157 << QStringLiteral( "List" )
158 << QStringLiteral( "ValueRelation" )
159 << QStringLiteral( "ValueMap" );
160
161 for ( int i = 0; i < fields.count(); ++i )
162 {
163 if ( ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) || mExcludedAttributeIndexes.contains( i ) )
164 continue;
165
166 QVariant val = feature.attributes().at( i );
167
168 if ( mLayer )
169 {
170 const QgsEditorWidgetSetup setup = fields.at( i ).editorWidgetSetup();
172 if ( formattersAllowList.contains( fieldFormatter->id() ) )
173 val = fieldFormatter->representValue( mLayer.data(), i, setup.config(), QVariant(), val );
174 }
175
176 QString name = fields.at( i ).name();
177 if ( mAttributeDisplayName )
178 {
179 name = mLayer->attributeDisplayName( i );
180 }
181 properties[ name.toStdString() ] = QgsJsonUtils::jsonFromVariant( val );
182 }
183 }
184
185 if ( !extraProperties.isEmpty() )
186 {
187 QVariantMap::const_iterator it = extraProperties.constBegin();
188 for ( ; it != extraProperties.constEnd(); ++it )
189 {
190 properties[ it.key().toStdString() ] = QgsJsonUtils::jsonFromVariant( it.value() );
191 }
192 }
193
194 // related attributes
195 if ( mLayer && mIncludeRelatedAttributes )
196 {
197 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer.data() );
198 for ( const auto &relation : std::as_const( relations ) )
199 {
200 QgsFeatureRequest req = relation.getRelatedFeaturesRequest( feature );
202 QgsVectorLayer *childLayer = relation.referencingLayer();
203 json relatedFeatureAttributes;
204 if ( childLayer )
205 {
206 QgsFeatureIterator it = childLayer->getFeatures( req );
207 QVector<QVariant> attributeWidgetCaches;
208 int fieldIndex = 0;
209 const QgsFields fields { childLayer->fields() };
210 for ( const QgsField &field : fields )
211 {
212 QgsEditorWidgetSetup setup = field.editorWidgetSetup();
214 attributeWidgetCaches.append( fieldFormatter->createCache( childLayer, fieldIndex, setup.config() ) );
215 fieldIndex++;
216 }
217 QgsFeature relatedFet;
218 while ( it.nextFeature( relatedFet ) )
219 {
220 relatedFeatureAttributes += QgsJsonUtils::exportAttributesToJsonObject( relatedFet, childLayer, attributeWidgetCaches );
221 }
222 }
223 properties[ relation.name().toStdString() ] = relatedFeatureAttributes;
224 }
225 }
226 }
227 featureJson[ "properties" ] = properties;
228 return featureJson;
229}
230
231QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features, int indent ) const
232{
233 return QString::fromStdString( exportFeaturesToJsonObject( features ).dump( indent ) );
234}
235
237{
238 json data
239 {
240 { "type", "FeatureCollection" },
241 { "features", json::array() }
242 };
243 for ( const QgsFeature &feature : std::as_const( features ) )
244 {
245 data["features"].push_back( exportFeatureToJsonObject( feature ) );
246 }
247 return data;
248}
249
251{
252 mDestinationCrs = destinationCrs;
253 mTransform.setDestinationCrs( mDestinationCrs );
254}
255
256//
257// QgsJsonUtils
258//
259
260QgsFeatureList QgsJsonUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
261{
262 if ( !encoding )
263 encoding = QTextCodec::codecForName( "UTF-8" );
264
265 return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
266}
267
268QgsFields QgsJsonUtils::stringToFields( const QString &string, QTextCodec *encoding )
269{
270 if ( !encoding )
271 encoding = QTextCodec::codecForName( "UTF-8" );
272
273 return QgsOgrUtils::stringToFields( string, encoding );
274}
275
276QString QgsJsonUtils::encodeValue( const QVariant &value )
277{
278 if ( QgsVariantUtils::isNull( value ) )
279 return QStringLiteral( "null" );
280
281 switch ( value.type() )
282 {
283 case QVariant::Int:
284 case QVariant::UInt:
285 case QVariant::LongLong:
286 case QVariant::ULongLong:
287 case QVariant::Double:
288 return value.toString();
289
290 case QVariant::Bool:
291 return value.toBool() ? "true" : "false";
292
293 case QVariant::StringList:
294 case QVariant::List:
295 case QVariant::Map:
296 return QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson( QJsonDocument::Compact ) );
297
298 default:
299 case QVariant::String:
300 QString v = value.toString()
301 .replace( '\\', QLatin1String( "\\\\" ) )
302 .replace( '"', QLatin1String( "\\\"" ) )
303 .replace( '\r', QLatin1String( "\\r" ) )
304 .replace( '\b', QLatin1String( "\\b" ) )
305 .replace( '\t', QLatin1String( "\\t" ) )
306 .replace( '/', QLatin1String( "\\/" ) )
307 .replace( '\n', QLatin1String( "\\n" ) );
308
309 return v.prepend( '"' ).append( '"' );
310 }
311}
312
313QString QgsJsonUtils::exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
314{
315 QgsFields fields = feature.fields();
316 QString attrs;
317 for ( int i = 0; i < fields.count(); ++i )
318 {
319 if ( i > 0 )
320 attrs += QLatin1String( ",\n" );
321
322 QVariant val = feature.attributes().at( i );
323
324 if ( layer )
325 {
326 QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
328 if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
329 val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
330 }
331
332 attrs += encodeValue( fields.at( i ).name() ) + ':' + encodeValue( val );
333 }
334 return attrs.prepend( '{' ).append( '}' );
335}
336
337QVariantList QgsJsonUtils::parseArray( const QString &json, QVariant::Type type )
338{
339 QString errorMessage;
340 QVariantList result;
341 try
342 {
343 const auto jObj( json::parse( json.toStdString() ) );
344 if ( ! jObj.is_array() )
345 {
346 throw json::parse_error::create( 0, 0, QStringLiteral( "JSON value must be an array" ).toStdString() );
347 }
348 for ( const auto &item : jObj )
349 {
350 // Create a QVariant from the array item
351 QVariant v;
352 if ( item.is_number_integer() )
353 {
354 v = item.get<int>();
355 }
356 else if ( item.is_number_unsigned() )
357 {
358 v = item.get<unsigned>();
359 }
360 else if ( item.is_number_float() )
361 {
362 // Note: it's a double and not a float on purpose
363 v = item.get<double>();
364 }
365 else if ( item.is_string() )
366 {
367 v = QString::fromStdString( item.get<std::string>() );
368 }
369 else if ( item.is_boolean() )
370 {
371 v = item.get<bool>();
372 }
373 else if ( item.is_null() )
374 {
375 // Fallback to int
376 v = QVariant( type == QVariant::Type::Invalid ? QVariant::Type::Int : type );
377 }
378
379 // If a destination type was specified (it's not invalid), try to convert
380 if ( type != QVariant::Invalid )
381 {
382 if ( ! v.convert( static_cast<int>( type ) ) )
383 {
384 QgsLogger::warning( QStringLiteral( "Cannot convert json array element to specified type, ignoring: %1" ).arg( v.toString() ) );
385 }
386 else
387 {
388 result.push_back( v );
389 }
390 }
391 else
392 {
393 result.push_back( v );
394 }
395 }
396 }
397 catch ( json::parse_error &ex )
398 {
399 errorMessage = ex.what();
400 QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( ex.what(), json ) );
401 }
402
403 return result;
404}
405
406json QgsJsonUtils::jsonFromVariant( const QVariant &val )
407{
408 if ( QgsVariantUtils::isNull( val ) )
409 {
410 return nullptr;
411 }
412 json j;
413 if ( val.type() == QVariant::Type::Map )
414 {
415 const QVariantMap &vMap = val.toMap();
416 json jMap = json::object();
417 for ( auto it = vMap.constBegin(); it != vMap.constEnd(); it++ )
418 {
419 jMap[ it.key().toStdString() ] = jsonFromVariant( it.value() );
420 }
421 j = jMap;
422 }
423 else if ( val.type() == QVariant::Type::List || val.type() == QVariant::Type::StringList )
424 {
425 const QVariantList &vList = val.toList();
426 json jList = json::array();
427 for ( const auto &v : vList )
428 {
429 jList.push_back( jsonFromVariant( v ) );
430 }
431 j = jList;
432 }
433 else
434 {
435 switch ( val.userType() )
436 {
437 case QMetaType::Int:
438 case QMetaType::UInt:
439 case QMetaType::LongLong:
440 case QMetaType::ULongLong:
441 j = val.toLongLong();
442 break;
443 case QMetaType::Double:
444 case QMetaType::Float:
445 j = val.toDouble();
446 break;
447 case QMetaType::Bool:
448 j = val.toBool();
449 break;
450 case QMetaType::QByteArray:
451 j = val.toByteArray().toBase64().toStdString();
452 break;
453 default:
454 j = val.toString().toStdString();
455 break;
456 }
457 }
458 return j;
459}
460
461QVariant QgsJsonUtils::parseJson( const std::string &jsonString )
462{
463 QString error;
464 const QVariant res = parseJson( jsonString, error );
465
466 if ( !error.isEmpty() )
467 {
468 QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( error,
469 QString::fromStdString( jsonString ) ) );
470 }
471 return res;
472}
473
474QVariant QgsJsonUtils::parseJson( const std::string &jsonString, QString &error )
475{
476 // tracks whether entire json string is a primitive
477 bool isPrimitive = true;
478
479 std::function<QVariant( json )> _parser { [ & ]( json jObj ) -> QVariant {
480 QVariant result;
481 if ( jObj.is_array() )
482 {
483 isPrimitive = false;
484 QVariantList results;
485 results.reserve( jObj.size() );
486 for ( const auto &item : jObj )
487 {
488 results.push_back( _parser( item ) );
489 }
490 result = results;
491 }
492 else if ( jObj.is_object() )
493 {
494 isPrimitive = false;
495 QVariantMap results;
496 for ( const auto &item : jObj.items() )
497 {
498 const auto key { QString::fromStdString( item.key() ) };
499 const auto value { _parser( item.value() ) };
500 results[ key ] = value;
501 }
502 result = results;
503 }
504 else
505 {
506 if ( jObj.is_number_integer() )
507 {
508 result = jObj.get<int>();
509 }
510 else if ( jObj.is_number_unsigned() )
511 {
512 result = jObj.get<unsigned>();
513 }
514 else if ( jObj.is_boolean() )
515 {
516 result = jObj.get<bool>();
517 }
518 else if ( jObj.is_number_float() )
519 {
520 // Note: it's a double and not a float on purpose
521 result = jObj.get<double>();
522 }
523 else if ( jObj.is_string() )
524 {
525 if ( isPrimitive && jObj.get<std::string>().length() == 0 )
526 {
527 result = QString::fromStdString( jObj.get<std::string>() ).append( "\"" ).insert( 0, "\"" );
528 }
529 else
530 {
531 result = QString::fromStdString( jObj.get<std::string>() );
532 }
533 }
534 else if ( jObj.is_null() )
535 {
536 // Do nothing (leave invalid)
537 }
538 }
539 return result;
540 }
541 };
542
543 error.clear();
544 try
545 {
546 const json j = json::parse( jsonString );
547 return _parser( j );
548 }
549 catch ( json::parse_error &ex )
550 {
551 error = QString::fromStdString( ex.what() );
552 }
553 return QVariant();
554}
555
556QVariant QgsJsonUtils::parseJson( const QString &jsonString )
557{
558 return parseJson( jsonString.toStdString() );
559}
560
561json QgsJsonUtils::exportAttributesToJsonObject( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
562{
563 QgsFields fields = feature.fields();
564 json attrs;
565 for ( int i = 0; i < fields.count(); ++i )
566 {
567 QVariant val = feature.attributes().at( i );
568
569 if ( layer )
570 {
571 QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
573 if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
574 val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
575 }
576 attrs[fields.at( i ).name().toStdString()] = jsonFromVariant( val );
577 }
578 return attrs;
579}
@ Success
Operation succeeded.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
Custom exception class for Coordinate Reference System related exceptions.
Holder for the widget type and its configuration for a field.
QVariantMap config() const
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:56
QgsAttributes attributes
Definition qgsfeature.h:65
QgsFields fields
Definition qgsfeature.h:66
QgsFeatureId id
Definition qgsfeature.h:64
QgsGeometry geometry
Definition qgsfeature.h:67
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString id() const =0
Returns a unique id for this field formatter.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QString name
Definition qgsfield.h:62
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:714
Container of fields for a vector layer.
Definition qgsfields.h:45
int count() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
virtual json asJsonObject(int precision=17) const
Exports the geometry to a json object.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
json exportFeatureToJsonObject(const QgsFeature &feature, const QVariantMap &extraProperties=QVariantMap(), const QVariant &id=QVariant()) const
Returns a QJsonObject representation of a feature.
json exportFeaturesToJsonObject(const QgsFeatureList &features) const
Returns a JSON object representation of a list of features (feature collection).
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source CRS for feature geometries.
void setDestinationCrs(const QgsCoordinateReferenceSystem &destinationCrs)
Set the destination CRS for feature geometry transformation to destinationCrs, this defaults to EPSG:...
QgsVectorLayer * vectorLayer() const
Returns the associated vector layer, if set.
QString exportFeature(const QgsFeature &feature, const QVariantMap &extraProperties=QVariantMap(), const QVariant &id=QVariant(), int indent=-1) const
Returns a GeoJSON string representation of a feature.
QString exportFeatures(const QgsFeatureList &features, int indent=-1) const
Returns a GeoJSON string representation of a list of features (feature collection).
void setVectorLayer(QgsVectorLayer *vectorLayer)
Sets the associated vector layer (required for related attribute export).
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source CRS for feature geometries.
QgsJsonExporter(QgsVectorLayer *vectorLayer=nullptr, int precision=6)
Constructor for QgsJsonExporter.
static QgsFeatureList stringToFeatureList(const QString &string, const QgsFields &fields=QgsFields(), QTextCodec *encoding=nullptr)
Attempts to parse a GeoJSON string to a collection of features.
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 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 json exportAttributesToJsonObject(const QgsFeature &feature, QgsVectorLayer *layer=nullptr, const QVector< QVariant > &attributeWidgetCaches=QVector< QVariant >())
Exports all attributes from a QgsFeature as a json object.
static QgsFields stringToFields(const QString &string, QTextCodec *encoding=nullptr)
Attempts to retrieve the fields from a GeoJSON string representing a collection of features.
static Q_INVOKABLE QVariantList parseArray(const QString &json, QVariant::Type type=QVariant::Invalid)
Parse a simple array (depth=1)
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
static void warning(const QString &msg)
Goes to qWarning.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:80
static QgsFeatureList stringToFeatureList(const QString &string, const QgsFields &fields, QTextCodec *encoding)
Attempts to parse a string representing a collection of features using OGR.
static QgsFields stringToFields(const QString &string, QTextCodec *encoding)
Attempts to retrieve the fields from a string representing a collection of features using OGR.
QgsRelationManager * relationManager
Definition qgsproject.h:117
static QgsProject * instance()
Returns the QgsProject singleton instance.
A rectangle specified with double values.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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:4386
QList< QgsFeature > QgsFeatureList
Definition qgsfeature.h:920
#define FID_IS_NULL(fid)
const QgsCoordinateReferenceSystem & crs
int precision