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