QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
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.
Definition: qgsattributes.h:59
This class represents a coordinate reference system (CRS).
Abstract base class for spatial data provider implementations.
QFlags< ReadFlag > ReadFlags
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).
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:230
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.
Definition: qgslogger.cpp:131
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 setNull()
Mark a rectangle as being null (holding no spatial information).
Definition: qgsrectangle.h:176
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.
QFlags< Capability > Capabilities
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...
Definition: qgswkbtypes.h:862
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
Definition: qgsattributes.h:45
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:42
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
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:27
QSet< int > QgsAttributeIds
const QgsCoordinateReferenceSystem & crs
const QString & typeName
int precision
const QgsAttributeList & attributeIndexes
Setting options for creating vector data providers.