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