QGIS API Documentation 3.36.0-Maidenhead (09951dc0acf)
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"
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( Qgis::CrsWktVariant::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.setNull();
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.setNull();
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
401QgsCoordinateReferenceSystem QgsMemoryProvider::crs() const
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 const int fieldIndex = it2.key();
679 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
680 continue;
681
682 QVariant attrValue = it2.value();
683 // Check attribute conversion
684 const bool conversionError { ! QgsVariantUtils::isNull( attrValue )
685 && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
686 if ( conversionError )
687 {
688 // Push first conversion error only
689 if ( result )
690 {
691 pushError( tr( "Could not change attribute %1 having type %2 for feature %4: %3" )
692 .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
693 errorMessage ).arg( it.key() ) );
694 }
695 result = false;
696 break;
697 }
698 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
699 fit->setAttribute( it2.key(), attrValue );
700 }
701 rollBackMap.insert( it.key(), rollBackAttrs );
702 }
703
704 // Roll back
705 if ( ! result )
706 {
707 changeAttributeValues( rollBackMap );
708 }
709 else
710 {
711 clearMinMaxCache();
712 }
713 return result;
714}
715
716bool QgsMemoryProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
717{
718 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
719 {
720 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
721 if ( fit == mFeatures.end() )
722 continue;
723
724 // update spatial index
725 if ( mSpatialIndex )
726 mSpatialIndex->deleteFeature( *fit );
727
728 fit->setGeometry( it.value() );
729
730 // update spatial index
731 if ( mSpatialIndex )
732 mSpatialIndex->addFeature( *fit );
733 }
734
735 updateExtents();
736
737 return true;
738}
739
740QString QgsMemoryProvider::subsetString() const
741{
742 return mSubsetString;
743}
744
745bool QgsMemoryProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount )
746{
747 Q_UNUSED( updateFeatureCount )
748
749 if ( !theSQL.isEmpty() )
750 {
751 const QgsExpression tempExpression( theSQL );
752 if ( tempExpression.hasParserError() )
753 return false;
754 }
755
756 if ( theSQL == mSubsetString )
757 return true;
758
759 mSubsetString = theSQL;
760 clearMinMaxCache();
761 mExtent.setNull();
762
763 emit dataChanged();
764 return true;
765}
766
767bool QgsMemoryProvider::createSpatialIndex()
768{
769 if ( !mSpatialIndex )
770 {
771 mSpatialIndex = new QgsSpatialIndex();
772
773 // add existing features to index
774 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
775 {
776 mSpatialIndex->addFeature( *it );
777 }
778 }
779 return true;
780}
781
782Qgis::SpatialIndexPresence QgsMemoryProvider::hasSpatialIndex() const
783{
785}
786
787QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities() const
788{
789 return AddFeatures | DeleteFeatures | ChangeGeometries |
790 ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
791 SelectAtId | CircularGeometries | FastTruncate;
792}
793
794bool QgsMemoryProvider::truncate()
795{
796 mFeatures.clear();
797 clearMinMaxCache();
798 mExtent.setNull();
799 return true;
800}
801
802void QgsMemoryProvider::updateExtents()
803{
804 mExtent.setNull();
805}
806
807QString QgsMemoryProvider::name() const
808{
809 return TEXT_PROVIDER_KEY;
810}
811
812QString QgsMemoryProvider::description() const
813{
814 return TEXT_PROVIDER_DESCRIPTION;
815}
816
817
818QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
819 : QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
820{
821}
822
823QIcon QgsMemoryProviderMetadata::icon() const
824{
825 return QgsApplication::getThemeIcon( QStringLiteral( "mIconMemory.svg" ) );
826}
827
828QgsDataProvider *QgsMemoryProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
829{
830 return new QgsMemoryProvider( uri, options, flags );
831}
832
833QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes() const
834{
835 return { Qgis::LayerType::Vector };
836}
837
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:349
@ NotPresent
No spatial index exists for the source.
@ Present
A valid spatial index exists for the source.
@ Vector
Vector layer.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:182
@ NoGeometry
No geometry.
@ Unknown
Unknown.
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
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.
This class represents a coordinate reference system (CRS).
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)
Fetch next feature and stores in f, returns true on success.
This class 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:56
QgsAttributes attributes
Definition qgsfeature.h:65
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
QgsGeometry geometry
Definition qgsfeature.h:67
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:53
QString typeName() const
Gets the field type.
Definition qgsfield.cpp:150
QString name
Definition qgsfield.h:62
int precision
Definition qgsfield.h:59
int length
Definition qgsfield.h:58
QVariant::Type type
Definition qgsfield.h:60
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:231
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.
Holds data provider key, description, and associated shared library file or function pointer informat...
A rectangle specified with double values.
void setNull()
Mark a rectangle as being null (holding no spatial information).
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, bool silenceNullWarnings=false)
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)
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 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
Definition qgsfeature.h:912
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
Definition qgsfeature.h:903
QList< QgsFeature > QgsFeatureList
Definition qgsfeature.h:917
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
Definition qgsfield.h:27
QSet< int > QgsAttributeIds
const QString & typeName
int precision
const QgsAttributeList & attributeIndexes
Setting options for creating vector data providers.