QGIS API Documentation 3.28.14-Firenze (exported)
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 {
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
400QgsCoordinateReferenceSystem QgsMemoryProvider::crs() const
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 const int fieldIndex = it2.key();
678 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
679 continue;
680
681 QVariant attrValue = it2.value();
682 // Check attribute conversion
683 const bool conversionError { ! QgsVariantUtils::isNull( attrValue )
684 && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
685 if ( conversionError )
686 {
687 // Push first conversion error only
688 if ( result )
689 {
690 pushError( tr( "Could not change attribute %1 having type %2 for feature %4: %3" )
691 .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
692 errorMessage ).arg( it.key() ) );
693 }
694 result = false;
695 break;
696 }
697 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
698 fit->setAttribute( it2.key(), attrValue );
699 }
700 rollBackMap.insert( it.key(), rollBackAttrs );
701 }
702
703 // Roll back
704 if ( ! result )
705 {
706 changeAttributeValues( rollBackMap );
707 }
708 else
709 {
710 clearMinMaxCache();
711 }
712 return result;
713}
714
715bool QgsMemoryProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
716{
717 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
718 {
719 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
720 if ( fit == mFeatures.end() )
721 continue;
722
723 // update spatial index
724 if ( mSpatialIndex )
725 mSpatialIndex->deleteFeature( *fit );
726
727 fit->setGeometry( it.value() );
728
729 // update spatial index
730 if ( mSpatialIndex )
731 mSpatialIndex->addFeature( *fit );
732 }
733
734 updateExtents();
735
736 return true;
737}
738
739QString QgsMemoryProvider::subsetString() const
740{
741 return mSubsetString;
742}
743
744bool QgsMemoryProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount )
745{
746 Q_UNUSED( updateFeatureCount )
747
748 if ( !theSQL.isEmpty() )
749 {
750 const QgsExpression tempExpression( theSQL );
751 if ( tempExpression.hasParserError() )
752 return false;
753 }
754
755 if ( theSQL == mSubsetString )
756 return true;
757
758 mSubsetString = theSQL;
759 clearMinMaxCache();
760 mExtent.setMinimal();
761
762 emit dataChanged();
763 return true;
764}
765
766bool QgsMemoryProvider::createSpatialIndex()
767{
768 if ( !mSpatialIndex )
769 {
770 mSpatialIndex = new QgsSpatialIndex();
771
772 // add existing features to index
773 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
774 {
775 mSpatialIndex->addFeature( *it );
776 }
777 }
778 return true;
779}
780
781QgsFeatureSource::SpatialIndexPresence QgsMemoryProvider::hasSpatialIndex() const
782{
783 return mSpatialIndex ? SpatialIndexPresent : SpatialIndexNotPresent;
784}
785
786QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities() const
787{
788 return AddFeatures | DeleteFeatures | ChangeGeometries |
789 ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
790 SelectAtId | CircularGeometries | FastTruncate;
791}
792
793bool QgsMemoryProvider::truncate()
794{
795 mFeatures.clear();
796 clearMinMaxCache();
797 mExtent.setMinimal();
798 return true;
799}
800
801void QgsMemoryProvider::updateExtents()
802{
803 mExtent.setMinimal();
804}
805
806QString QgsMemoryProvider::name() const
807{
808 return TEXT_PROVIDER_KEY;
809}
810
811QString QgsMemoryProvider::description() const
812{
813 return TEXT_PROVIDER_DESCRIPTION;
814}
815
816
817QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
818 : QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
819{
820}
821
822QIcon QgsMemoryProviderMetadata::icon() const
823{
824 return QgsApplication::getThemeIcon( QStringLiteral( "mIconMemory.svg" ) );
825}
826
827QgsDataProvider *QgsMemoryProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
828{
829 return new QgsMemoryProvider( uri, options, flags );
830}
831
832QList<QgsMapLayerType> QgsMemoryProviderMetadata::supportedLayerTypes() const
833{
835}
836
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).
@ 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).
@ RollBackOnErrors
Roll back the whole transaction if a single add feature operation fails.
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.
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:51
QString typeName() const
Gets the field type.
Definition qgsfield.cpp:152
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:147
void setTypeName(const QString &typeName)
Set the field type.
Definition qgsfield.cpp:203
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 setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
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 Type parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Type
The WKB type describes the number of dimensions a geometry has.
Definition qgswkbtypes.h:70
static QString displayString(Type type)
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
QMap< int, QVariant > QgsAttributeMap
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
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
Definition qgsfield.h:26
const QgsField & field
Definition qgsfield.h:476
QSet< int > QgsAttributeIds
const QString & typeName
int precision
const QgsAttributeList & attributeIndexes
Setting options for creating vector data providers.