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