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