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