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