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 );
125 NativeType(
QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::Int ), u
"integerlist"_s, QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::Int )
127 NativeType(
QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::Double ), u
"doublelist"_s, QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::Double )
129 NativeType(
QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::LongLong ), u
"integer64list"_s, QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::LongLong )
136 if ( query.hasQueryItem( u
"field"_s ) )
138 QList<QgsField> attributes;
139 const thread_local QRegularExpression reFieldDef(
146 QRegularExpression::CaseInsensitiveOption
148 const QStringList fields = query.allQueryItemValues( u
"field"_s );
149 for (
int i = 0; i < fields.size(); i++ )
151 QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
152 const QRegularExpressionMatch regularExpressionMatch = reFieldDef.match( name );
155 QMetaType::Type type = QMetaType::Type::QString;
156 QMetaType::Type subType = QMetaType::Type::UnknownType;
157 QString typeName( u
"string"_s );
161 if ( regularExpressionMatch.hasMatch() )
163 name = name.mid( 0, regularExpressionMatch.capturedStart() );
164 typeName = regularExpressionMatch.captured( 1 ).toLower();
167 bool isNativeType =
false;
168 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
169 for (
const NativeType &nativeType : nativeTypesList )
171 if ( nativeType.mTypeName.toLower() == typeName )
174 type = nativeType.mType;
175 subType = nativeType.mSubType;
176 typeName = nativeType.mTypeName;
182 if ( isNativeType ==
false )
184 if ( typeName ==
"int"_L1 )
186 type = QMetaType::Type::Int;
187 typeName = u
"integer"_s;
189 else if ( typeName ==
"long"_L1 )
191 type = QMetaType::Type::LongLong;
192 typeName = u
"int8"_s;
194 else if ( typeName ==
"bool"_L1 )
196 type = QMetaType::Type::Bool;
197 typeName = u
"boolean"_s;
201 QgsLogger::warning( tr(
"Unsupported typeName '%1'. Will be handled as string." ).arg( typeName ) );
202 type = QMetaType::Type::QString;
203 typeName = u
"string"_s;
208 if ( typeName ==
"real"_L1 || typeName ==
"double"_L1 )
214 if ( !regularExpressionMatch.captured( 2 ).isEmpty() )
215 length = regularExpressionMatch.captured( 2 ).toInt();
217 if ( !regularExpressionMatch.captured( 3 ).isEmpty() )
218 precision = regularExpressionMatch.captured( 3 ).toInt();
221 if ( !regularExpressionMatch.captured( 4 ).isEmpty() )
223 if ( subType == QMetaType::Type::UnknownType )
226 if ( type != QMetaType::Type::QVariantList && type != QMetaType::Type::QStringList )
227 type = type == QMetaType::Type::QString ? QMetaType::Type::QStringList : QMetaType::Type::QVariantList;
229 const QLatin1String listSuffix(
"list" );
230 if ( !typeName.endsWith( listSuffix ) )
231 typeName +=
"list"_L1;
235 attributes.append(
QgsField( name, type, typeName, length, precision, QString(), subType ) );
237 addAttributes( attributes );
240 if ( query.hasQueryItem( u
"index"_s ) && query.queryItemValue( u
"index"_s ) ==
"yes"_L1 )
242 createSpatialIndex();
246QgsMemoryProvider::~QgsMemoryProvider()
249QString QgsMemoryProvider::providerKey()
251 return TEXT_PROVIDER_KEY;
254QString QgsMemoryProvider::providerDescription()
256 return TEXT_PROVIDER_DESCRIPTION;
261 return new QgsMemoryFeatureSource(
this );
264QString QgsMemoryProvider::dataSourceUri(
bool expandAuthConfig )
const
266 Q_UNUSED( expandAuthConfig )
268 QUrl uri( u
"memory"_s );
271 query.addQueryItem( u
"geometry"_s, geometry );
273 if ( mCrs.isValid() )
276 const QString authid = mCrs.authid();
277 if ( authid.startsWith(
"EPSG:"_L1 ) )
285 query.addQueryItem( u
"crs"_s, crsDef );
289 query.addQueryItem( u
"index"_s, u
"yes"_s );
292 QgsAttributeList attrs =
const_cast<QgsMemoryProvider *
>( this )->attributeIndexes();
293 for (
int i = 0; i < attrs.size(); i++ )
295 const QgsField field = mFields.at( attrs[i] );
296 QString fieldDef = field.
name();
298 QString typeName = field.
typeName();
300 if ( field.
type() == QMetaType::Type::QVariantList || field.
type() == QMetaType::Type::QStringList )
304 case QMetaType::Type::Int:
305 typeName = u
"integer"_s;
308 case QMetaType::Type::LongLong:
309 typeName = u
"long"_s;
312 case QMetaType::Type::Double:
313 typeName = u
"double"_s;
316 case QMetaType::Type::QString:
317 typeName = u
"string"_s;
326 fieldDef.append( u
":%2(%3,%4)%5"_s.arg( typeName ).arg( field.
length() ).arg( field.
precision() ).arg( isList ? u
"[]"_s : QString() ) );
327 query.addQueryItem( u
"field"_s, fieldDef );
329 uri.setQuery( query );
331 return QString( uri.toEncoded() );
334QString QgsMemoryProvider::storageType()
const
336 return u
"Memory storage"_s;
341 return QgsFeatureIterator(
new QgsMemoryFeatureIterator(
new QgsMemoryFeatureSource(
this ),
true, request ) );
347 return extent3D().toRectangle();
350QgsBox3D QgsMemoryProvider::extent3D()
const
352 if ( mExtent3D.isEmpty() && !mFeatures.isEmpty() )
355 if ( mSubsetString.isEmpty() )
358 const auto constMFeatures = mFeatures;
359 for (
const QgsFeature &feat : constMFeatures )
361 if ( feat.hasGeometry() )
362 mExtent3D.combineWith( feat.geometry().boundingBox3D() );
376 else if ( mFeatures.isEmpty() )
389long long QgsMemoryProvider::featureCount()
const
391 if ( mSubsetString.isEmpty() )
392 return mFeatures.count();
405QgsFields QgsMemoryProvider::fields()
const
410bool QgsMemoryProvider::isValid()
const
423 if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
426 mFeatures = other->mFeatures;
427 mNextFeatureId = other->mNextFeatureId;
428 mExtent3D = other->mExtent3D;
433bool QgsMemoryProvider::addFeatures(
QgsFeatureList &flist, Flags flags )
437 const bool updateExtent = mFeatures.isEmpty() || !mExtent3D.isEmpty();
439 const int fieldCount = mFields.count();
442 const auto oldExtent3D { mExtent3D };
443 const auto oldNextFeatureId { mNextFeatureId };
446 for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result; ++it )
448 it->setId( mNextFeatureId );
449 it->setValid(
true );
450 const int attributeCount = it->attributeCount();
451 if ( attributeCount < fieldCount )
456 for (
int i = attributeCount; i < mFields.count(); ++i )
460 it->setAttributes( attributes );
462 else if ( attributeCount > fieldCount )
465 pushError( tr(
"Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( attributeCount ) );
467 attributes.resize( mFields.count() );
468 it->setAttributes( attributes );
483 bool conversionError {
false };
484 QString errorMessage;
485 for (
int i = 0; i < mFields.count(); ++i )
487 const QVariant originalValue = it->attribute( i );
488 QVariant attrValue = originalValue;
494 pushError( tr(
"Could not store attribute \"%1\": %2" ).arg( mFields.at( i ).name(), errorMessage ) );
497 conversionError =
true;
500 else if ( attrValue.userType() != originalValue.userType() )
503 it->setAttribute( i, attrValue );
508 if ( conversionError )
517 mFeatures.insert( mNextFeatureId, *it );
518 addedFids.insert( mNextFeatureId );
520 if ( it->hasGeometry() )
524 mExtent3D.combineWith( it->geometry().boundingBox3D() );
529 mSpatialIndex->addFeature( *it );
540 mFeatures.remove( addedFid );
542 mExtent3D = oldExtent3D;
543 mNextFeatureId = oldNextFeatureId;
553bool QgsMemoryProvider::deleteFeatures(
const QgsFeatureIds &
id )
555 for ( QgsFeatureIds::const_iterator it =
id.begin(); it !=
id.end(); ++it )
557 const QgsFeatureMap::iterator fit = mFeatures.find( *it );
560 if ( fit == mFeatures.end() )
565 mSpatialIndex->deleteFeature( *fit );
567 mFeatures.erase( fit );
576bool QgsMemoryProvider::addAttributes(
const QList<QgsField> &attributes )
578 bool fieldWasAdded {
false };
581 if ( !supportedType( field ) )
585 bool isNativeTypeName =
false;
586 NativeType nativeTypeCandidate( QString(), QString(), QMetaType::Type::UnknownType );
587 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
588 for (
const NativeType &nativeType : nativeTypesList )
590 if ( nativeType.mTypeName.toLower() == field.
typeName().toLower() )
592 isNativeTypeName =
true;
596 if ( nativeType.mType == field.
type() && nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
597 nativeTypeCandidate = nativeType;
599 if ( !isNativeTypeName )
601 if ( nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
607 field.
setTypeName( nativeTypeCandidate.mTypeName );
611 mFields.append( field );
612 fieldWasAdded =
true;
614 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
618 attr.append( QVariant() );
622 return fieldWasAdded;
625bool QgsMemoryProvider::renameAttributes(
const QgsFieldNameMap &renamedAttributes )
627 QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
629 for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
631 const int fieldIndex = renameIt.key();
632 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
637 if ( mFields.indexFromName( renameIt.value() ) >= 0 )
644 mFields.rename( fieldIndex, renameIt.value() );
649bool QgsMemoryProvider::deleteAttributes(
const QgsAttributeIds &attributes )
651 QList<int> attrIdx( attributes.begin(), attributes.end() );
652 std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
655 for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
658 mFields.remove( idx );
660 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
674 bool result {
true };
678 QString errorMessage;
679 for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
681 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
682 if ( fit == mFeatures.end() )
689 for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
691 const int fieldIndex = it2.key();
692 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
695 QVariant attrValue = it2.value();
700 const bool conversionError { !
QgsVariantUtils::isNull( attrValue ) && !mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
701 if ( conversionError )
706 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() ) );
711 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
712 fit->setAttribute( it2.key(), attrValue );
714 rollBackMap.insert( it.key(), rollBackAttrs );
720 changeAttributeValues( rollBackMap );
729bool QgsMemoryProvider::changeGeometryValues(
const QgsGeometryMap &geometry_map )
731 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
733 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
734 if ( fit == mFeatures.end() )
739 mSpatialIndex->deleteFeature( *fit );
741 fit->setGeometry( it.value() );
745 mSpatialIndex->addFeature( *fit );
753QString QgsMemoryProvider::subsetString()
const
755 return mSubsetString;
758bool QgsMemoryProvider::setSubsetString(
const QString &theSQL,
bool updateFeatureCount )
760 Q_UNUSED( updateFeatureCount )
762 if ( !theSQL.isEmpty() )
765 if ( tempExpression.hasParserError() )
769 if ( theSQL == mSubsetString )
772 mSubsetString = theSQL;
780bool QgsMemoryProvider::supportsSubsetString()
const
785QString QgsMemoryProvider::subsetStringDialect()
const
787 return tr(
"QGIS expression" );
790QString QgsMemoryProvider::subsetStringHelpUrl()
const
796bool QgsMemoryProvider::createSpatialIndex()
798 if ( !mSpatialIndex )
800 mSpatialIndex = std::make_unique<QgsSpatialIndex>();
803 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
805 mSpatialIndex->addFeature( *it );
835bool QgsMemoryProvider::truncate()
843void QgsMemoryProvider::updateExtents()
848QString QgsMemoryProvider::name()
const
850 return TEXT_PROVIDER_KEY;
853QString QgsMemoryProvider::description()
const
855 return TEXT_PROVIDER_DESCRIPTION;
859QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
860 :
QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
863QIcon QgsMemoryProviderMetadata::icon()
const
870 return new QgsMemoryProvider( uri, options, flags );
873QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes()
const
878#undef TEXT_PROVIDER_KEY
879#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.
A 3-dimensional box composed of x, y, z coordinates.
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.
QgsBox3D boundingBox3D() const
Returns the 3D 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...
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
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.