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