QGIS API Documentation 4.1.0-Master (376402f9aeb)
Loading...
Searching...
No Matches
qgsmemoryprovider.cpp
Go to the documentation of this file.
1/***************************************************************************
2 memoryprovider.cpp - provider with storage in memory
3 ------------------
4 begin : June 2008
5 copyright : (C) 2008 by Martin Dobias
6 email : wonder dot sk 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 "qgsmemoryprovider.h"
17
18#include "qgsapplication.h"
20#include "qgsfeature.h"
21#include "qgsfields.h"
22#include "qgsgeometry.h"
23#include "qgslogger.h"
25#include "qgsspatialindex.h"
26#include "qgsvariantutils.h"
27
28#include <QIcon>
29#include <QRegularExpression>
30#include <QString>
31#include <QUrl>
32#include <QUrlQuery>
33
34#include "moc_qgsmemoryprovider.cpp"
35
36using namespace Qt::StringLiterals;
37
39
40#define TEXT_PROVIDER_KEY u"memory"_s
41#define TEXT_PROVIDER_DESCRIPTION u"Memory provider"_s
42
43QgsMemoryProvider::QgsMemoryProvider( const QString &uri, const ProviderOptions &options, Qgis::DataProviderReadFlags flags )
44 : QgsVectorDataProvider( uri, options, flags )
45{
46 // Initialize the geometry with the uri to support old style uri's
47 // (ie, just 'point', 'line', 'polygon')
48 const QUrl url = QUrl::fromEncoded( uri.toUtf8() );
49 const QUrlQuery query( url );
50 QString geometry;
51 if ( query.hasQueryItem( u"geometry"_s ) )
52 {
53 geometry = query.queryItemValue( u"geometry"_s );
54 }
55 else
56 {
57 geometry = url.path();
58 }
59
60 if ( geometry.compare( "none"_L1, Qt::CaseInsensitive ) == 0 )
61 {
63 }
64 else
65 {
66 mWkbType = QgsWkbTypes::parseType( geometry );
67 }
68
69 if ( query.hasQueryItem( u"crs"_s ) )
70 {
71 const QString crsDef = query.queryItemValue( u"crs"_s );
72 mCrs.createFromString( crsDef );
73 }
74 else
75 {
76 // TODO - remove in QGIS 5.0. Layers without an explicit CRS set SHOULD have an invalid CRS. But in order to maintain
77 // 3.x api, we have to be tolerant/shortsighted(?) here and fallback to EPSG:4326
78 mCrs = QgsCoordinateReferenceSystem( u"EPSG:4326"_s );
79 }
80
81 elevationProperties()->setContainsElevationData( QgsWkbTypes::hasZ( mWkbType ) );
82
83 mNextFeatureId = 1;
84
85 setNativeTypes(
86 QList< NativeType >()
87 << QgsVectorDataProvider::NativeType( tr( "Whole Number (integer)" ), u"integer"_s, QMetaType::Type::Int, 0, 10 )
88 // Decimal number from OGR/Shapefile/dbf may come with length up to 32 and
89 // precision up to length-2 = 30 (default, if width is not specified in dbf is length = 24 precision = 15)
90 // We know that double (QVariant::Double) has only 15-16 significant numbers,
91 // but setting that correct limits would disable the use of memory provider with
92 // data from Shapefiles. In any case, the data are handled as doubles.
93 // So the limits set here are not correct but enable use of data from Shapefiles.
94 << QgsVectorDataProvider::NativeType( tr( "Decimal Number (real)" ), u"double"_s, QMetaType::Type::Double, 0, 32, 0, 30 )
95 << QgsVectorDataProvider::NativeType( tr( "Text (string)" ), u"string"_s, QMetaType::Type::QString, 0, 255 )
96
97 // date type
98 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDate ), u"date"_s, QMetaType::Type::QDate, -1, -1, -1, -1 )
99 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QTime ), u"time"_s, QMetaType::Type::QTime, -1, -1, -1, -1 )
100 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDateTime ), u"datetime"_s, QMetaType::Type::QDateTime, -1, -1, -1, -1 )
101
102 // integer types
103 << QgsVectorDataProvider::NativeType( tr( "Whole Number (smallint - 16bit)" ), u"int2"_s, QMetaType::Type::Int, -1, -1, 0, 0 )
104 << QgsVectorDataProvider::NativeType( tr( "Whole Number (integer - 32bit)" ), u"int4"_s, QMetaType::Type::Int, -1, -1, 0, 0 )
105 << QgsVectorDataProvider::NativeType( tr( "Whole Number (integer - 64bit)" ), u"int8"_s, QMetaType::Type::LongLong, -1, -1, 0, 0 )
106 << QgsVectorDataProvider::NativeType( tr( "Decimal Number (numeric)" ), u"numeric"_s, QMetaType::Type::Double, 1, 20, 0, 20 )
107 << QgsVectorDataProvider::NativeType( tr( "Decimal Number (decimal)" ), u"decimal"_s, QMetaType::Type::Double, 1, 20, 0, 20 )
108
109 // floating point
110 << QgsVectorDataProvider::NativeType( tr( "Decimal Number (real)" ), u"real"_s, QMetaType::Type::Double, -1, -1, -1, -1 )
111 << QgsVectorDataProvider::NativeType( tr( "Decimal Number (double)" ), u"double precision"_s, QMetaType::Type::Double, -1, -1, -1, -1 )
112
113 // string types
114 << QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (text)" ), u"text"_s, QMetaType::Type::QString, -1, -1, -1, -1 )
115
116 // boolean
117 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Bool ), u"boolean"_s, QMetaType::Type::Bool )
118
119 // blob
120 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QByteArray ), u"binary"_s, QMetaType::Type::QByteArray )
121
122 // list types
123 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QStringList ), u"stringlist"_s, QMetaType::Type::QStringList, 0, 0, 0, 0, QMetaType::Type::QString )
125 NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::Int ), u"integerlist"_s, QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::Int )
127 NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::Double ), u"doublelist"_s, QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::Double )
129 NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::LongLong ), u"integer64list"_s, QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::LongLong )
130
131 // complex types
132 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantMap ), u"map"_s, QMetaType::Type::QVariantMap, -1, -1, -1, -1 )
133 << QgsVectorDataProvider::NativeType( tr( "Geometry" ), u"geometry"_s, QMetaType::Type::User )
134 );
135
136 if ( query.hasQueryItem( u"field"_s ) )
137 {
138 QList<QgsField> attributes;
139 const thread_local QRegularExpression reFieldDef(
140 "\\:"
141 "([\\w\\s]+)" // type
142 "(?:\\((\\-?\\d+)" // length
143 "(?:\\,(\\-?\\d+))?" // precision
144 "\\))?(\\[\\])?" // array
145 "$",
146 QRegularExpression::CaseInsensitiveOption
147 );
148 const QStringList fields = query.allQueryItemValues( u"field"_s );
149 for ( int i = 0; i < fields.size(); i++ )
150 {
151 QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
152 const QRegularExpressionMatch regularExpressionMatch = reFieldDef.match( name );
153
154 // If no match -> use string as type
155 QMetaType::Type type = QMetaType::Type::QString;
156 QMetaType::Type subType = QMetaType::Type::UnknownType;
157 QString typeName( u"string"_s );
158 int length = 255;
159 int precision = 0;
160
161 if ( regularExpressionMatch.hasMatch() )
162 {
163 name = name.mid( 0, regularExpressionMatch.capturedStart() );
164 typeName = regularExpressionMatch.captured( 1 ).toLower();
165
166 // Search typeName correspondence in native types
167 bool isNativeType = false;
168 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
169 for ( const NativeType &nativeType : nativeTypesList )
170 {
171 if ( nativeType.mTypeName.toLower() == typeName )
172 {
173 isNativeType = true;
174 type = nativeType.mType;
175 subType = nativeType.mSubType;
176 typeName = nativeType.mTypeName;
177 break;
178 }
179 }
180
181 // Not a native type -> check other supported types:
182 if ( isNativeType == false )
183 {
184 if ( typeName == "int"_L1 )
185 {
186 type = QMetaType::Type::Int;
187 typeName = u"integer"_s;
188 }
189 else if ( typeName == "long"_L1 )
190 {
191 type = QMetaType::Type::LongLong;
192 typeName = u"int8"_s;
193 }
194 else if ( typeName == "bool"_L1 )
195 {
196 type = QMetaType::Type::Bool;
197 typeName = u"boolean"_s;
198 }
199 else
200 {
201 QgsLogger::warning( tr( "Unsupported typeName '%1'. Will be handled as string." ).arg( typeName ) );
202 type = QMetaType::Type::QString;
203 typeName = u"string"_s;
204 }
205 }
206
207 // Set default length/precision for double/real
208 if ( typeName == "real"_L1 || typeName == "double"_L1 )
209 {
210 length = 20;
211 precision = 5;
212 }
213
214 if ( !regularExpressionMatch.captured( 2 ).isEmpty() )
215 length = regularExpressionMatch.captured( 2 ).toInt();
216
217 if ( !regularExpressionMatch.captured( 3 ).isEmpty() )
218 precision = regularExpressionMatch.captured( 3 ).toInt();
219
220 // Array
221 if ( !regularExpressionMatch.captured( 4 ).isEmpty() )
222 {
223 if ( subType == QMetaType::Type::UnknownType )
224 subType = type;
225
226 if ( type != QMetaType::Type::QVariantList && type != QMetaType::Type::QStringList )
227 type = type == QMetaType::Type::QString ? QMetaType::Type::QStringList : QMetaType::Type::QVariantList;
228
229 const QLatin1String listSuffix( "list" );
230 if ( !typeName.endsWith( listSuffix ) )
231 typeName += "list"_L1;
232 }
233 }
234
235 attributes.append( QgsField( name, type, typeName, length, precision, QString(), subType ) );
236 }
237 addAttributes( attributes );
238 }
239
240 if ( query.hasQueryItem( u"index"_s ) && query.queryItemValue( u"index"_s ) == "yes"_L1 )
241 {
242 createSpatialIndex();
243 }
244}
245
246QgsMemoryProvider::~QgsMemoryProvider()
247{}
248
249QString QgsMemoryProvider::providerKey()
250{
251 return TEXT_PROVIDER_KEY;
252}
253
254QString QgsMemoryProvider::providerDescription()
255{
256 return TEXT_PROVIDER_DESCRIPTION;
257}
258
259QgsAbstractFeatureSource *QgsMemoryProvider::featureSource() const
260{
261 return new QgsMemoryFeatureSource( this );
262}
263
264QString QgsMemoryProvider::dataSourceUri( bool expandAuthConfig ) const
265{
266 Q_UNUSED( expandAuthConfig )
267
268 QUrl uri( u"memory"_s );
269 QUrlQuery query;
270 const QString geometry = QgsWkbTypes::displayString( mWkbType );
271 query.addQueryItem( u"geometry"_s, geometry );
272
273 if ( mCrs.isValid() )
274 {
275 QString crsDef;
276 const QString authid = mCrs.authid();
277 if ( authid.startsWith( "EPSG:"_L1 ) )
278 {
279 crsDef = authid;
280 }
281 else
282 {
283 crsDef = u"wkt:%1"_s.arg( mCrs.toWkt( Qgis::CrsWktVariant::Preferred ) );
284 }
285 query.addQueryItem( u"crs"_s, crsDef );
286 }
287 if ( mSpatialIndex )
288 {
289 query.addQueryItem( u"index"_s, u"yes"_s );
290 }
291
292 QgsAttributeList attrs = const_cast<QgsMemoryProvider *>( this )->attributeIndexes();
293 for ( int i = 0; i < attrs.size(); i++ )
294 {
295 const QgsField field = mFields.at( attrs[i] );
296 QString fieldDef = field.name();
297
298 QString typeName = field.typeName();
299 bool isList = false;
300 if ( field.type() == QMetaType::Type::QVariantList || field.type() == QMetaType::Type::QStringList )
301 {
302 switch ( field.subType() )
303 {
304 case QMetaType::Type::Int:
305 typeName = u"integer"_s;
306 break;
307
308 case QMetaType::Type::LongLong:
309 typeName = u"long"_s;
310 break;
311
312 case QMetaType::Type::Double:
313 typeName = u"double"_s;
314 break;
315
316 case QMetaType::Type::QString:
317 typeName = u"string"_s;
318 break;
319
320 default:
321 break;
322 }
323 isList = true;
324 }
325
326 fieldDef.append( u":%2(%3,%4)%5"_s.arg( typeName ).arg( field.length() ).arg( field.precision() ).arg( isList ? u"[]"_s : QString() ) );
327 query.addQueryItem( u"field"_s, fieldDef );
328 }
329 uri.setQuery( query );
330
331 return QString( uri.toEncoded() );
332}
333
334QString QgsMemoryProvider::storageType() const
335{
336 return u"Memory storage"_s;
337}
338
339QgsFeatureIterator QgsMemoryProvider::getFeatures( const QgsFeatureRequest &request ) const
340{
341 return QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, request ) );
342}
343
344
345QgsRectangle QgsMemoryProvider::extent() const
346{
347 return extent3D().toRectangle();
348}
349
350QgsBox3D QgsMemoryProvider::extent3D() const
351{
352 if ( mExtent3D.isEmpty() && !mFeatures.isEmpty() )
353 {
354 mExtent3D.setNull();
355 if ( mSubsetString.isEmpty() )
356 {
357 // fast way - iterate through all features
358 const auto constMFeatures = mFeatures;
359 for ( const QgsFeature &feat : constMFeatures )
360 {
361 if ( feat.hasGeometry() )
362 mExtent3D.combineWith( feat.geometry().boundingBox3D() );
363 }
364 }
365 else
366 {
367 QgsFeature feat;
368 QgsFeatureIterator featIt = getFeatures( QgsFeatureRequest().setNoAttributes() );
369 while ( featIt.nextFeature( feat ) )
370 {
371 if ( feat.hasGeometry() )
372 mExtent3D.combineWith( feat.geometry().boundingBox3D() );
373 }
374 }
375 }
376 else if ( mFeatures.isEmpty() )
377 {
378 mExtent3D.setNull();
379 }
380
381 return mExtent3D;
382}
383
384Qgis::WkbType QgsMemoryProvider::wkbType() const
385{
386 return mWkbType;
387}
388
389long long QgsMemoryProvider::featureCount() const
390{
391 if ( mSubsetString.isEmpty() )
392 return mFeatures.count();
393
394 // subset string set, no alternative but testing each feature
395 QgsFeatureIterator fit = QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, QgsFeatureRequest().setNoAttributes() ) );
396 long long count = 0;
397 QgsFeature feature;
398 while ( fit.nextFeature( feature ) )
399 {
400 count++;
401 }
402 return count;
403}
404
405QgsFields QgsMemoryProvider::fields() const
406{
407 return mFields;
408}
409
410bool QgsMemoryProvider::isValid() const
411{
412 return ( mWkbType != Qgis::WkbType::Unknown );
413}
414
415QgsCoordinateReferenceSystem QgsMemoryProvider::crs() const
416{
417 // TODO: make provider projection-aware
418 return mCrs; // return default CRS
419}
420
421void QgsMemoryProvider::handlePostCloneOperations( QgsVectorDataProvider *source )
422{
423 if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
424 {
425 // these properties aren't copied when cloning a memory provider by uri, so we need to do it manually
426 mFeatures = other->mFeatures;
427 mNextFeatureId = other->mNextFeatureId;
428 mExtent3D = other->mExtent3D;
429 }
430}
431
432// returns TRUE if all features were added successfully, or FALSE if any feature could not be added
433bool QgsMemoryProvider::addFeatures( QgsFeatureList &flist, Flags flags )
434{
435 bool result = true;
436 // whether or not to update the layer extent on the fly as we add features
437 const bool updateExtent = mFeatures.isEmpty() || !mExtent3D.isEmpty();
438
439 const int fieldCount = mFields.count();
440
441 // For rollback
442 const auto oldExtent3D { mExtent3D };
443 const auto oldNextFeatureId { mNextFeatureId };
444 QgsFeatureIds addedFids;
445
446 for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result; ++it )
447 {
448 it->setId( mNextFeatureId );
449 it->setValid( true );
450 const int attributeCount = it->attributeCount();
451 if ( attributeCount < fieldCount )
452 {
453 // ensure features have the correct number of attributes by padding
454 // them with null attributes for missing values
455 QgsAttributes attributes = it->attributes();
456 for ( int i = attributeCount; i < mFields.count(); ++i )
457 {
458 attributes.append( QgsVariantUtils::createNullVariant( mFields.at( i ).type() ) );
459 }
460 it->setAttributes( attributes );
461 }
462 else if ( attributeCount > fieldCount )
463 {
464 // too many attributes
465 pushError( tr( "Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( attributeCount ) );
466 QgsAttributes attributes = it->attributes();
467 attributes.resize( mFields.count() );
468 it->setAttributes( attributes );
469 }
470
471 if ( it->hasGeometry() && mWkbType == Qgis::WkbType::NoGeometry )
472 {
473 it->clearGeometry();
474 }
475 else if ( it->hasGeometry() && QgsWkbTypes::geometryType( it->geometry().wkbType() ) != QgsWkbTypes::geometryType( mWkbType ) )
476 {
477 pushError( tr( "Could not add feature with geometry type %1 to layer of type %2" ).arg( QgsWkbTypes::displayString( it->geometry().wkbType() ), QgsWkbTypes::displayString( mWkbType ) ) );
478 result = false;
479 continue;
480 }
481
482 // Check attribute conversion
483 bool conversionError { false };
484 QString errorMessage;
485 for ( int i = 0; i < mFields.count(); ++i )
486 {
487 const QVariant originalValue = it->attribute( i );
488 QVariant attrValue = originalValue;
489 if ( !QgsVariantUtils::isNull( attrValue ) && !mFields.at( i ).convertCompatible( attrValue, &errorMessage ) )
490 {
491 // Push first conversion error only
492 if ( result )
493 {
494 pushError( tr( "Could not store attribute \"%1\": %2" ).arg( mFields.at( i ).name(), errorMessage ) );
495 }
496 result = false;
497 conversionError = true;
498 continue;
499 }
500 else if ( attrValue.userType() != originalValue.userType() )
501 {
502 // convertCompatible has resulted in a data type conversion
503 it->setAttribute( i, attrValue );
504 }
505 }
506
507 // Skip the feature if there is at least one conversion error
508 if ( conversionError )
509 {
510 if ( flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
511 {
512 break;
513 }
514 continue;
515 }
516
517 mFeatures.insert( mNextFeatureId, *it );
518 addedFids.insert( mNextFeatureId );
519
520 if ( it->hasGeometry() )
521 {
522 if ( updateExtent )
523 {
524 mExtent3D.combineWith( it->geometry().boundingBox3D() );
525 }
526
527 // update spatial index
528 if ( mSpatialIndex )
529 mSpatialIndex->addFeature( *it );
530 }
531
532 mNextFeatureId++;
533 }
534
535 // Roll back
536 if ( !result && flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
537 {
538 for ( const QgsFeatureId &addedFid : addedFids )
539 {
540 mFeatures.remove( addedFid );
541 }
542 mExtent3D = oldExtent3D;
543 mNextFeatureId = oldNextFeatureId;
544 }
545 else
546 {
547 clearMinMaxCache();
548 }
549
550 return result;
551}
552
553bool QgsMemoryProvider::deleteFeatures( const QgsFeatureIds &id )
554{
555 for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
556 {
557 const QgsFeatureMap::iterator fit = mFeatures.find( *it );
558
559 // check whether such feature exists
560 if ( fit == mFeatures.end() )
561 continue;
562
563 // update spatial index
564 if ( mSpatialIndex )
565 mSpatialIndex->deleteFeature( *fit );
566
567 mFeatures.erase( fit );
568 }
569
570 updateExtents();
571 clearMinMaxCache();
572
573 return true;
574}
575
576bool QgsMemoryProvider::addAttributes( const QList<QgsField> &attributes )
577{
578 bool fieldWasAdded { false };
579 for ( QgsField field : attributes )
580 {
581 if ( !supportedType( field ) )
582 continue;
583
584 // Make sure added attributes typeName correspond to a native type name
585 bool isNativeTypeName = false;
586 NativeType nativeTypeCandidate( QString(), QString(), QMetaType::Type::UnknownType );
587 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
588 for ( const NativeType &nativeType : nativeTypesList )
589 {
590 if ( nativeType.mTypeName.toLower() == field.typeName().toLower() )
591 {
592 isNativeTypeName = true;
593 break;
594 }
595
596 if ( nativeType.mType == field.type() && nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
597 nativeTypeCandidate = nativeType;
598 }
599 if ( !isNativeTypeName )
600 {
601 if ( nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
602 {
603 QgsLogger::warning( "Field type not supported: " + field.typeName() );
604 continue;
605 }
606
607 field.setTypeName( nativeTypeCandidate.mTypeName );
608 }
609
610 // add new field as a last one
611 mFields.append( field );
612 fieldWasAdded = true;
613
614 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
615 {
616 QgsFeature &f = fit.value();
617 QgsAttributes attr = f.attributes();
618 attr.append( QVariant() );
619 f.setAttributes( attr );
620 }
621 }
622 return fieldWasAdded;
623}
624
625bool QgsMemoryProvider::renameAttributes( const QgsFieldNameMap &renamedAttributes )
626{
627 QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
628 bool result = true;
629 for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
630 {
631 const int fieldIndex = renameIt.key();
632 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
633 {
634 result = false;
635 continue;
636 }
637 if ( mFields.indexFromName( renameIt.value() ) >= 0 )
638 {
639 //field name already in use
640 result = false;
641 continue;
642 }
643
644 mFields.rename( fieldIndex, renameIt.value() );
645 }
646 return result;
647}
648
649bool QgsMemoryProvider::deleteAttributes( const QgsAttributeIds &attributes )
650{
651 QList<int> attrIdx( attributes.begin(), attributes.end() );
652 std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
653
654 // delete attributes one-by-one with decreasing index
655 for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
656 {
657 const int idx = *it;
658 mFields.remove( idx );
659
660 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
661 {
662 QgsFeature &f = fit.value();
663 QgsAttributes attr = f.attributes();
664 attr.remove( idx );
665 f.setAttributes( attr );
666 }
667 }
668 clearMinMaxCache();
669 return true;
670}
671
672bool QgsMemoryProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
673{
674 bool result { true };
675
676 QgsChangedAttributesMap rollBackMap;
677
678 QString errorMessage;
679 for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
680 {
681 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
682 if ( fit == mFeatures.end() )
683 continue;
684
685 const QgsAttributeMap &attrs = it.value();
686 QgsAttributeMap rollBackAttrs;
687
688 // Break on errors
689 for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
690 {
691 const int fieldIndex = it2.key();
692 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
693 continue;
694
695 QVariant attrValue = it2.value();
697 continue;
698
699 // Check attribute conversion
700 const bool conversionError { !QgsVariantUtils::isNull( attrValue ) && !mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
701 if ( conversionError )
702 {
703 // Push first conversion error only
704 if ( result )
705 {
706 pushError( tr( "Could not change attribute %1 having type %2 for feature %4: %3" ).arg( mFields.at( it2.key() ).name(), it2.value().typeName(), errorMessage ).arg( it.key() ) );
707 }
708 result = false;
709 break;
710 }
711 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
712 fit->setAttribute( it2.key(), attrValue );
713 }
714 rollBackMap.insert( it.key(), rollBackAttrs );
715 }
716
717 // Roll back
718 if ( !result )
719 {
720 changeAttributeValues( rollBackMap );
721 }
722 else
723 {
724 clearMinMaxCache();
725 }
726 return result;
727}
728
729bool QgsMemoryProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
730{
731 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
732 {
733 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
734 if ( fit == mFeatures.end() )
735 continue;
736
737 // update spatial index
738 if ( mSpatialIndex )
739 mSpatialIndex->deleteFeature( *fit );
740
741 fit->setGeometry( it.value() );
742
743 // update spatial index
744 if ( mSpatialIndex )
745 mSpatialIndex->addFeature( *fit );
746 }
747
748 updateExtents();
749
750 return true;
751}
752
753QString QgsMemoryProvider::subsetString() const
754{
755 return mSubsetString;
756}
757
758bool QgsMemoryProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount )
759{
760 Q_UNUSED( updateFeatureCount )
761
762 if ( !theSQL.isEmpty() )
763 {
764 const QgsExpression tempExpression( theSQL );
765 if ( tempExpression.hasParserError() )
766 return false;
767 }
768
769 if ( theSQL == mSubsetString )
770 return true;
771
772 mSubsetString = theSQL;
773 clearMinMaxCache();
774 mExtent3D.setNull();
775
776 emit dataChanged();
777 return true;
778}
779
780bool QgsMemoryProvider::supportsSubsetString() const
781{
782 return true;
783}
784
785QString QgsMemoryProvider::subsetStringDialect() const
786{
787 return tr( "QGIS expression" );
788}
789
790QString QgsMemoryProvider::subsetStringHelpUrl() const
791{
792 // unfortunately we can't access QgsHelp here, that's a GUI class!
793 return QString();
794}
795
796bool QgsMemoryProvider::createSpatialIndex()
797{
798 if ( !mSpatialIndex )
799 {
800 mSpatialIndex = std::make_unique<QgsSpatialIndex>();
801
802 // add existing features to index
803 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
804 {
805 mSpatialIndex->addFeature( *it );
806 }
807 }
808 return true;
809}
810
811Qgis::SpatialIndexPresence QgsMemoryProvider::hasSpatialIndex() const
812{
814}
815
816Qgis::VectorProviderCapabilities QgsMemoryProvider::capabilities() const
817{
827 };
828 if ( mWkbType != Qgis::WkbType::NoGeometry )
829 {
831 }
832 return caps;
833}
834
835bool QgsMemoryProvider::truncate()
836{
837 mFeatures.clear();
838 clearMinMaxCache();
839 mExtent3D.setNull();
840 return true;
841}
842
843void QgsMemoryProvider::updateExtents()
844{
845 mExtent3D.setNull();
846}
847
848QString QgsMemoryProvider::name() const
849{
850 return TEXT_PROVIDER_KEY;
851}
852
853QString QgsMemoryProvider::description() const
854{
855 return TEXT_PROVIDER_DESCRIPTION;
856}
857
858
859QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
860 : QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
861{}
862
863QIcon QgsMemoryProviderMetadata::icon() const
864{
865 return QgsApplication::getThemeIcon( u"mIconMemory.svg"_s );
866}
867
868QgsDataProvider *QgsMemoryProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
869{
870 return new QgsMemoryProvider( uri, options, flags );
871}
872
873QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes() const
874{
875 return { Qgis::LayerType::Vector };
876}
877
878#undef TEXT_PROVIDER_KEY
879#undef TEXT_PROVIDER_DESCRIPTION
880
@ SelectAtId
Fast access to features using their ID.
Definition qgis.h:533
@ FastTruncate
Supports fast truncation of the layer (removing all features).
Definition qgis.h:543
@ AddFeatures
Allows adding features.
Definition qgis.h:527
@ CircularGeometries
Supports circular geometry types (circularstring, compoundcurve, curvepolygon).
Definition qgis.h:540
@ ChangeGeometries
Allows modifications of geometries.
Definition qgis.h:534
@ AddAttributes
Allows addition of new attributes (fields).
Definition qgis.h:530
@ CreateSpatialIndex
Allows creation of spatial index.
Definition qgis.h:532
@ RenameAttributes
Supports renaming attributes (fields).
Definition qgis.h:542
@ DeleteFeatures
Allows deletion of features.
Definition qgis.h:528
@ DeleteAttributes
Allows deletion of attributes (fields).
Definition qgis.h:531
@ ChangeAttributeValues
Allows modification of attribute values.
Definition qgis.h:529
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:585
@ NotPresent
No spatial index exists for the source.
Definition qgis.h:587
@ Present
A valid spatial index exists for the source.
Definition qgis.h:588
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:512
QFlags< VectorProviderCapability > VectorProviderCapabilities
Vector data provider capabilities.
Definition qgis.h:563
@ Vector
Vector layer.
Definition qgis.h:207
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ NoGeometry
No geometry.
Definition qgis.h:312
@ Unknown
Unknown.
Definition qgis.h:295
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Definition qgis.h:2580
Base class that can be used for any class that is capable of returning features.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
A vector of attributes.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
Represents a coordinate reference system (CRS).
Abstract base class for spatial data provider implementations.
Handles parsing and evaluation of expressions (formerly called "search strings").
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).
@ RollBackOnErrors
Roll back the whole transaction if a single add feature operation fails.
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:69
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
QgsGeometry geometry
Definition qgsfeature.h:71
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QMetaType::Type type
Definition qgsfield.h:63
QString typeName() const
Gets the field type.
Definition qgsfield.cpp:158
QString name
Definition qgsfield.h:65
int precision
Definition qgsfield.h:62
int length
Definition qgsfield.h:61
QMetaType::Type subType() const
If the field is a collection, gets its element's type.
Definition qgsfield.cpp:153
void setTypeName(const QString &typeName)
Set the field type.
Definition qgsfield.cpp:249
Container of fields for a vector layer.
Definition qgsfields.h:46
QgsBox3D boundingBox3D() const
Returns the 3D bounding box of the geometry.
static void warning(const QString &msg)
Goes to qWarning.
Holds data provider key, description, and associated shared library file or function pointer informat...
A rectangle specified with double values.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QString typeToDisplayString(QMetaType::Type type, QMetaType::Type subType=QMetaType::Type::UnknownType)
Returns a user-friendly translated string representing a QVariant type.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
static bool isUnsetAttributeValue(const QVariant &variant)
Check if the variant is a QgsUnsetAttributeValue.
Base class for vector data providers.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Qgis::WkbType parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
static Q_INVOKABLE QString displayString(Qgis::WkbType type)
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
QMap< int, QString > QgsFieldNameMap
QMap< int, QVariant > QgsAttributeMap
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
QList< QgsFeature > QgsFeatureList
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
Definition qgsfield.h:30
QSet< int > QgsAttributeIds
Setting options for creating vector data providers.