QGIS API Documentation 3.39.0-Master (bca3cdb6021)
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, Qgis::DataProviderReadFlags 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::supportsSubsetString() const
769{
770 return true;
771}
772
773QString QgsMemoryProvider::subsetStringDialect() const
774{
775 return tr( "QGIS expression" );
776}
777
778QString QgsMemoryProvider::subsetStringHelpUrl() const
779{
780 // unfortunately we can't access QgsHelp here, that's a GUI class!
781 return QString();
782}
783
784bool QgsMemoryProvider::createSpatialIndex()
785{
786 if ( !mSpatialIndex )
787 {
788 mSpatialIndex = new QgsSpatialIndex();
789
790 // add existing features to index
791 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
792 {
793 mSpatialIndex->addFeature( *it );
794 }
795 }
796 return true;
797}
798
799Qgis::SpatialIndexPresence QgsMemoryProvider::hasSpatialIndex() const
800{
802}
803
804Qgis::VectorProviderCapabilities QgsMemoryProvider::capabilities() const
805{
809}
810
811bool QgsMemoryProvider::truncate()
812{
813 mFeatures.clear();
814 clearMinMaxCache();
815 mExtent.setNull();
816 return true;
817}
818
819void QgsMemoryProvider::updateExtents()
820{
821 mExtent.setNull();
822}
823
824QString QgsMemoryProvider::name() const
825{
826 return TEXT_PROVIDER_KEY;
827}
828
829QString QgsMemoryProvider::description() const
830{
831 return TEXT_PROVIDER_DESCRIPTION;
832}
833
834
835QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
836 : QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
837{
838}
839
840QIcon QgsMemoryProviderMetadata::icon() const
841{
842 return QgsApplication::getThemeIcon( QStringLiteral( "mIconMemory.svg" ) );
843}
844
845QgsDataProvider *QgsMemoryProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
846{
847 return new QgsMemoryProvider( uri, options, flags );
848}
849
850QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes() const
851{
852 return { Qgis::LayerType::Vector };
853}
854
@ SelectAtId
Fast access to features using their ID.
@ FastTruncate
Supports fast truncation of the layer (removing all features)
@ AddFeatures
Allows adding features.
@ CircularGeometries
Supports circular geometry types (circularstring, compoundcurve, curvepolygon)
@ ChangeGeometries
Allows modifications of geometries.
@ AddAttributes
Allows addition of new attributes (fields)
@ CreateSpatialIndex
Allows creation of spatial index.
@ RenameAttributes
Supports renaming attributes (fields)
@ DeleteFeatures
Allows deletion of features.
@ DeleteAttributes
Allows deletion of attributes (fields)
@ ChangeAttributeValues
Allows modification of attribute values.
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:522
@ NotPresent
No spatial index exists for the source.
@ Present
A valid spatial index exists for the source.
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:450
QFlags< VectorProviderCapability > VectorProviderCapabilities
Vector data provider capabilities.
Definition qgis.h:500
@ Vector
Vector layer.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:256
@ 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.
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.
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.