29#include <QRegularExpression>
34#include "moc_qgsmemoryprovider.cpp"
36using namespace Qt::StringLiterals;
40#define TEXT_PROVIDER_KEY u"memory"_s
41#define TEXT_PROVIDER_DESCRIPTION u"Memory provider"_s
48 const QUrl url = QUrl::fromEncoded( uri.toUtf8() );
49 const QUrlQuery query( url );
51 if ( query.hasQueryItem( u
"geometry"_s ) )
53 geometry = query.queryItemValue( u
"geometry"_s );
57 geometry = url.path();
60 if ( geometry.compare(
"none"_L1, Qt::CaseInsensitive ) == 0 )
69 if ( query.hasQueryItem( u
"crs"_s ) )
71 const QString crsDef = query.queryItemValue( u
"crs"_s );
72 mCrs.createFromString( crsDef );
123 NativeType(
QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::Int ), u
"integerlist"_s, QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::Int )
125 NativeType(
QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::Double ), u
"doublelist"_s, QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::Double )
127 NativeType(
QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::LongLong ), u
"integer64list"_s, QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::LongLong )
134 if ( query.hasQueryItem( u
"field"_s ) )
136 QList<QgsField> attributes;
137 const thread_local QRegularExpression reFieldDef(
144 QRegularExpression::CaseInsensitiveOption
146 const QStringList fields = query.allQueryItemValues( u
"field"_s );
147 for (
int i = 0; i < fields.size(); i++ )
149 QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
150 const QRegularExpressionMatch regularExpressionMatch = reFieldDef.match( name );
153 QMetaType::Type type = QMetaType::Type::QString;
154 QMetaType::Type subType = QMetaType::Type::UnknownType;
155 QString typeName( u
"string"_s );
159 if ( regularExpressionMatch.hasMatch() )
161 name = name.mid( 0, regularExpressionMatch.capturedStart() );
162 typeName = regularExpressionMatch.captured( 1 ).toLower();
165 bool isNativeType =
false;
166 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
167 for (
const NativeType &nativeType : nativeTypesList )
169 if ( nativeType.mTypeName.toLower() == typeName )
172 type = nativeType.mType;
173 subType = nativeType.mSubType;
174 typeName = nativeType.mTypeName;
180 if ( isNativeType ==
false )
182 if ( typeName ==
"int"_L1 )
184 type = QMetaType::Type::Int;
185 typeName = u
"integer"_s;
187 else if ( typeName ==
"long"_L1 )
189 type = QMetaType::Type::LongLong;
190 typeName = u
"int8"_s;
192 else if ( typeName ==
"bool"_L1 )
194 type = QMetaType::Type::Bool;
195 typeName = u
"boolean"_s;
199 QgsLogger::warning( tr(
"Unsupported typeName '%1'. Will be handled as string." ).arg( typeName ) );
200 type = QMetaType::Type::QString;
201 typeName = u
"string"_s;
206 if ( typeName ==
"real"_L1 || typeName ==
"double"_L1 )
212 if ( !regularExpressionMatch.captured( 2 ).isEmpty() )
213 length = regularExpressionMatch.captured( 2 ).toInt();
215 if ( !regularExpressionMatch.captured( 3 ).isEmpty() )
216 precision = regularExpressionMatch.captured( 3 ).toInt();
219 if ( !regularExpressionMatch.captured( 4 ).isEmpty() )
221 if ( subType == QMetaType::Type::UnknownType )
224 if ( type != QMetaType::Type::QVariantList && type != QMetaType::Type::QStringList )
225 type = type == QMetaType::Type::QString ? QMetaType::Type::QStringList : QMetaType::Type::QVariantList;
227 const QLatin1String listSuffix(
"list" );
228 if ( !typeName.endsWith( listSuffix ) )
229 typeName +=
"list"_L1;
233 attributes.append(
QgsField( name, type, typeName, length, precision, QString(), subType ) );
235 addAttributes( attributes );
238 if ( query.hasQueryItem( u
"index"_s ) && query.queryItemValue( u
"index"_s ) ==
"yes"_L1 )
240 createSpatialIndex();
244QgsMemoryProvider::~QgsMemoryProvider()
247QString QgsMemoryProvider::providerKey()
249 return TEXT_PROVIDER_KEY;
252QString QgsMemoryProvider::providerDescription()
254 return TEXT_PROVIDER_DESCRIPTION;
259 return new QgsMemoryFeatureSource(
this );
262QString QgsMemoryProvider::dataSourceUri(
bool expandAuthConfig )
const
264 Q_UNUSED( expandAuthConfig )
266 QUrl uri( u
"memory"_s );
269 query.addQueryItem( u
"geometry"_s, geometry );
271 if ( mCrs.isValid() )
274 const QString authid = mCrs.authid();
275 if ( authid.startsWith(
"EPSG:"_L1 ) )
283 query.addQueryItem( u
"crs"_s, crsDef );
287 query.addQueryItem( u
"index"_s, u
"yes"_s );
290 QgsAttributeList attrs =
const_cast<QgsMemoryProvider *
>( this )->attributeIndexes();
291 for (
int i = 0; i < attrs.size(); i++ )
293 const QgsField field = mFields.at( attrs[i] );
294 QString fieldDef = field.
name();
296 QString typeName = field.
typeName();
298 if ( field.
type() == QMetaType::Type::QVariantList || field.
type() == QMetaType::Type::QStringList )
302 case QMetaType::Type::Int:
303 typeName = u
"integer"_s;
306 case QMetaType::Type::LongLong:
307 typeName = u
"long"_s;
310 case QMetaType::Type::Double:
311 typeName = u
"double"_s;
314 case QMetaType::Type::QString:
315 typeName = u
"string"_s;
324 fieldDef.append( u
":%2(%3,%4)%5"_s.arg( typeName ).arg( field.
length() ).arg( field.
precision() ).arg( isList ? u
"[]"_s : QString() ) );
325 query.addQueryItem( u
"field"_s, fieldDef );
327 uri.setQuery( query );
329 return QString( uri.toEncoded() );
332QString QgsMemoryProvider::storageType()
const
334 return u
"Memory storage"_s;
339 return QgsFeatureIterator(
new QgsMemoryFeatureIterator(
new QgsMemoryFeatureSource(
this ),
true, request ) );
345 if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
348 if ( mSubsetString.isEmpty() )
351 const auto constMFeatures = mFeatures;
352 for (
const QgsFeature &feat : constMFeatures )
354 if ( feat.hasGeometry() )
355 mExtent.combineExtentWith( feat.geometry().boundingBox() );
369 else if ( mFeatures.isEmpty() )
382long long QgsMemoryProvider::featureCount()
const
384 if ( mSubsetString.isEmpty() )
385 return mFeatures.count();
398QgsFields QgsMemoryProvider::fields()
const
403bool QgsMemoryProvider::isValid()
const
416 if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
419 mFeatures = other->mFeatures;
420 mNextFeatureId = other->mNextFeatureId;
421 mExtent = other->mExtent;
426bool QgsMemoryProvider::addFeatures(
QgsFeatureList &flist, Flags flags )
430 const bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
432 const int fieldCount = mFields.count();
435 const auto oldExtent { mExtent };
436 const auto oldNextFeatureId { mNextFeatureId };
439 for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result; ++it )
441 it->setId( mNextFeatureId );
442 it->setValid(
true );
443 const int attributeCount = it->attributeCount();
444 if ( attributeCount < fieldCount )
449 for (
int i = attributeCount; i < mFields.count(); ++i )
453 it->setAttributes( attributes );
455 else if ( attributeCount > fieldCount )
458 pushError( tr(
"Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( attributeCount ) );
460 attributes.resize( mFields.count() );
461 it->setAttributes( attributes );
476 bool conversionError {
false };
477 QString errorMessage;
478 for (
int i = 0; i < mFields.count(); ++i )
480 const QVariant originalValue = it->attribute( i );
481 QVariant attrValue = originalValue;
487 pushError( tr(
"Could not store attribute \"%1\": %2" ).arg( mFields.at( i ).name(), errorMessage ) );
490 conversionError =
true;
493 else if ( attrValue.userType() != originalValue.userType() )
496 it->setAttribute( i, attrValue );
501 if ( conversionError )
510 mFeatures.insert( mNextFeatureId, *it );
511 addedFids.insert( mNextFeatureId );
513 if ( it->hasGeometry() )
516 mExtent.combineExtentWith( it->geometry().boundingBox() );
520 mSpatialIndex->addFeature( *it );
531 mFeatures.remove( addedFid );
534 mNextFeatureId = oldNextFeatureId;
544bool QgsMemoryProvider::deleteFeatures(
const QgsFeatureIds &
id )
546 for ( QgsFeatureIds::const_iterator it =
id.begin(); it !=
id.end(); ++it )
548 const QgsFeatureMap::iterator fit = mFeatures.find( *it );
551 if ( fit == mFeatures.end() )
556 mSpatialIndex->deleteFeature( *fit );
558 mFeatures.erase( fit );
567bool QgsMemoryProvider::addAttributes(
const QList<QgsField> &attributes )
569 bool fieldWasAdded {
false };
572 if ( !supportedType( field ) )
576 bool isNativeTypeName =
false;
577 NativeType nativeTypeCandidate( QString(), QString(), QMetaType::Type::UnknownType );
578 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
579 for (
const NativeType &nativeType : nativeTypesList )
581 if ( nativeType.mTypeName.toLower() == field.
typeName().toLower() )
583 isNativeTypeName =
true;
587 if ( nativeType.mType == field.
type() && nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
588 nativeTypeCandidate = nativeType;
590 if ( !isNativeTypeName )
592 if ( nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
598 field.
setTypeName( nativeTypeCandidate.mTypeName );
602 mFields.append( field );
603 fieldWasAdded =
true;
605 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
609 attr.append( QVariant() );
613 return fieldWasAdded;
616bool QgsMemoryProvider::renameAttributes(
const QgsFieldNameMap &renamedAttributes )
618 QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
620 for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
622 const int fieldIndex = renameIt.key();
623 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
628 if ( mFields.indexFromName( renameIt.value() ) >= 0 )
635 mFields.rename( fieldIndex, renameIt.value() );
640bool QgsMemoryProvider::deleteAttributes(
const QgsAttributeIds &attributes )
642 QList<int> attrIdx( attributes.begin(), attributes.end() );
643 std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
646 for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
649 mFields.remove( idx );
651 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
665 bool result {
true };
669 QString errorMessage;
670 for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
672 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
673 if ( fit == mFeatures.end() )
680 for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
682 const int fieldIndex = it2.key();
683 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
686 QVariant attrValue = it2.value();
691 const bool conversionError { !
QgsVariantUtils::isNull( attrValue ) && !mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
692 if ( conversionError )
697 pushError( tr(
"Could not change attribute %1 having type %2 for feature %4: %3" ).arg( mFields.at( it2.key() ).name(), it2.value().typeName(), errorMessage ).arg( it.key() ) );
702 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
703 fit->setAttribute( it2.key(), attrValue );
705 rollBackMap.insert( it.key(), rollBackAttrs );
711 changeAttributeValues( rollBackMap );
720bool QgsMemoryProvider::changeGeometryValues(
const QgsGeometryMap &geometry_map )
722 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
724 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
725 if ( fit == mFeatures.end() )
730 mSpatialIndex->deleteFeature( *fit );
732 fit->setGeometry( it.value() );
736 mSpatialIndex->addFeature( *fit );
744QString QgsMemoryProvider::subsetString()
const
746 return mSubsetString;
749bool QgsMemoryProvider::setSubsetString(
const QString &theSQL,
bool updateFeatureCount )
751 Q_UNUSED( updateFeatureCount )
753 if ( !theSQL.isEmpty() )
756 if ( tempExpression.hasParserError() )
760 if ( theSQL == mSubsetString )
763 mSubsetString = theSQL;
771bool QgsMemoryProvider::supportsSubsetString()
const
776QString QgsMemoryProvider::subsetStringDialect()
const
778 return tr(
"QGIS expression" );
781QString QgsMemoryProvider::subsetStringHelpUrl()
const
787bool QgsMemoryProvider::createSpatialIndex()
789 if ( !mSpatialIndex )
791 mSpatialIndex = std::make_unique<QgsSpatialIndex>();
794 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
796 mSpatialIndex->addFeature( *it );
826bool QgsMemoryProvider::truncate()
834void QgsMemoryProvider::updateExtents()
839QString QgsMemoryProvider::name()
const
841 return TEXT_PROVIDER_KEY;
844QString QgsMemoryProvider::description()
const
846 return TEXT_PROVIDER_DESCRIPTION;
850QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
851 :
QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
854QIcon QgsMemoryProviderMetadata::icon()
const
861 return new QgsMemoryProvider( uri, options, flags );
864QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes()
const
869#undef TEXT_PROVIDER_KEY
870#undef TEXT_PROVIDER_DESCRIPTION
@ 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.
@ 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.
QFlags< VectorProviderCapability > VectorProviderCapabilities
Vector data provider capabilities.
WkbType
The WKB type describes the number of dimensions a geometry has.
@ 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.
Represents a coordinate reference system (CRS).
Abstract base class for spatial data provider implementations.
Handles parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
@ RollBackOnErrors
Roll back the whole transaction if a single add feature operation fails.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Encapsulate a field in an attribute table or data source.
QString typeName() const
Gets the field type.
QMetaType::Type subType() const
If the field is a collection, gets its element's type.
void setTypeName(const QString &typeName)
Set the field type.
Container of fields for a vector layer.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static void warning(const QString &msg)
Goes to qWarning.
A rectangle specified with double values.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QString typeToDisplayString(QMetaType::Type type, QMetaType::Type subType=QMetaType::Type::UnknownType)
Returns a user-friendly translated string representing a QVariant type.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
static bool isUnsetAttributeValue(const QVariant &variant)
Check if the variant is a QgsUnsetAttributeValue.
Base class for vector data providers.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Qgis::WkbType parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
static Q_INVOKABLE QString displayString(Qgis::WkbType type)
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
QMap< int, QString > QgsFieldNameMap
QMap< int, QVariant > QgsAttributeMap
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
QList< QgsFeature > QgsFeatureList
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
QSet< int > QgsAttributeIds
Setting options for creating vector data providers.