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 );
83 setNativeTypes( QList< NativeType >()
130 if ( query.hasQueryItem( u
"field"_s ) )
132 QList<QgsField> attributes;
133 const thread_local QRegularExpression reFieldDef(
"\\:"
139 QRegularExpression::CaseInsensitiveOption );
140 const QStringList fields = query.allQueryItemValues( u
"field"_s );
141 for (
int i = 0; i < fields.size(); i++ )
143 QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
144 const QRegularExpressionMatch regularExpressionMatch = reFieldDef.match( name );
147 QMetaType::Type type = QMetaType::Type::QString;
148 QMetaType::Type subType = QMetaType::Type::UnknownType;
149 QString typeName( u
"string"_s );
153 if ( regularExpressionMatch.hasMatch() )
155 name = name.mid( 0, regularExpressionMatch.capturedStart() );
156 typeName = regularExpressionMatch.captured( 1 ).toLower();
159 bool isNativeType =
false;
160 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
161 for (
const NativeType &nativeType : nativeTypesList )
163 if ( nativeType.mTypeName.toLower() == typeName )
166 type = nativeType.mType;
167 subType = nativeType.mSubType;
168 typeName = nativeType.mTypeName;
174 if ( isNativeType ==
false )
176 if ( typeName ==
"int"_L1 )
178 type = QMetaType::Type::Int;
179 typeName = u
"integer"_s;
181 else if ( typeName ==
"long"_L1 )
183 type = QMetaType::Type::LongLong;
184 typeName = u
"int8"_s;
186 else if ( typeName ==
"bool"_L1 )
188 type = QMetaType::Type::Bool;
189 typeName = u
"boolean"_s;
193 QgsLogger::warning( tr(
"Unsupported typeName '%1'. Will be handled as string." ).arg( typeName ) );
194 type = QMetaType::Type::QString;
195 typeName = u
"string"_s;
200 if ( typeName ==
"real"_L1 || typeName ==
"double"_L1 )
206 if ( !regularExpressionMatch.captured( 2 ).isEmpty() )
207 length = regularExpressionMatch.captured( 2 ).toInt();
209 if ( !regularExpressionMatch.captured( 3 ).isEmpty() )
210 precision = regularExpressionMatch.captured( 3 ).toInt();
213 if ( !regularExpressionMatch.captured( 4 ).isEmpty() )
215 if ( subType == QMetaType::Type::UnknownType )
218 if ( type != QMetaType::Type::QVariantList && type != QMetaType::Type::QStringList )
219 type = type == QMetaType::Type::QString ? QMetaType::Type::QStringList : QMetaType::Type::QVariantList;
221 const QLatin1String listSuffix(
"list" );
222 if ( !typeName.endsWith( listSuffix ) )
223 typeName +=
"list"_L1;
227 attributes.append(
QgsField( name, type, typeName, length, precision, QString(), subType ) );
229 addAttributes( attributes );
232 if ( query.hasQueryItem( u
"index"_s ) && query.queryItemValue( u
"index"_s ) ==
"yes"_L1 )
234 createSpatialIndex();
239QgsMemoryProvider::~QgsMemoryProvider()
244QString QgsMemoryProvider::providerKey()
246 return TEXT_PROVIDER_KEY;
249QString QgsMemoryProvider::providerDescription()
251 return TEXT_PROVIDER_DESCRIPTION;
256 return new QgsMemoryFeatureSource(
this );
259QString QgsMemoryProvider::dataSourceUri(
bool expandAuthConfig )
const
261 Q_UNUSED( expandAuthConfig )
263 QUrl uri( u
"memory"_s );
266 query.addQueryItem( u
"geometry"_s, geometry );
268 if ( mCrs.isValid() )
271 const QString authid = mCrs.authid();
272 if ( authid.startsWith(
"EPSG:"_L1 ) )
280 query.addQueryItem( u
"crs"_s, crsDef );
284 query.addQueryItem( u
"index"_s, u
"yes"_s );
287 QgsAttributeList attrs =
const_cast<QgsMemoryProvider *
>( this )->attributeIndexes();
288 for (
int i = 0; i < attrs.size(); i++ )
290 const QgsField field = mFields.at( attrs[i] );
291 QString fieldDef = field.
name();
293 QString typeName = field.
typeName();
295 if ( field.
type() == QMetaType::Type::QVariantList || field.
type() == QMetaType::Type::QStringList )
299 case QMetaType::Type::Int:
300 typeName = u
"integer"_s;
303 case QMetaType::Type::LongLong:
304 typeName = u
"long"_s;
307 case QMetaType::Type::Double:
308 typeName = u
"double"_s;
311 case QMetaType::Type::QString:
312 typeName = u
"string"_s;
321 fieldDef.append( u
":%2(%3,%4)%5"_s.arg( typeName ).arg( field.
length() ).arg( field.
precision() ).arg( isList ? u
"[]"_s : QString() ) );
322 query.addQueryItem( u
"field"_s, fieldDef );
324 uri.setQuery( query );
326 return QString( uri.toEncoded() );
330QString QgsMemoryProvider::storageType()
const
332 return u
"Memory storage"_s;
337 return QgsFeatureIterator(
new QgsMemoryFeatureIterator(
new QgsMemoryFeatureSource(
this ),
true, request ) );
343 if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
346 if ( mSubsetString.isEmpty() )
349 const auto constMFeatures = mFeatures;
350 for (
const QgsFeature &feat : constMFeatures )
352 if ( feat.hasGeometry() )
353 mExtent.combineExtentWith( feat.geometry().boundingBox() );
367 else if ( mFeatures.isEmpty() )
380long long QgsMemoryProvider::featureCount()
const
382 if ( mSubsetString.isEmpty() )
383 return mFeatures.count();
396QgsFields QgsMemoryProvider::fields()
const
401bool QgsMemoryProvider::isValid()
const
414 if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
417 mFeatures = other->mFeatures;
418 mNextFeatureId = other->mNextFeatureId;
419 mExtent = other->mExtent;
424bool QgsMemoryProvider::addFeatures(
QgsFeatureList &flist, Flags flags )
428 const bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
430 const int fieldCount = mFields.count();
433 const auto oldExtent { mExtent };
434 const auto oldNextFeatureId { mNextFeatureId };
437 for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result ; ++it )
439 it->setId( mNextFeatureId );
440 it->setValid(
true );
441 const int attributeCount = it->attributeCount();
442 if ( attributeCount < fieldCount )
447 for (
int i = attributeCount; i < mFields.count(); ++i )
451 it->setAttributes( attributes );
453 else if ( attributeCount > fieldCount )
456 pushError( tr(
"Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( attributeCount ) );
458 attributes.resize( mFields.count() );
459 it->setAttributes( attributes );
469 pushError( tr(
"Could not add feature with geometry type %1 to layer of type %2" ).arg(
QgsWkbTypes::displayString( it->geometry().wkbType() ),
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;
482 if ( !
QgsVariantUtils::isNull( attrValue ) && ! mFields.at( i ).convertCompatible( attrValue, &errorMessage ) )
487 pushError( tr(
"Could not store attribute \"%1\": %2" )
488 .arg( mFields.at( i ).name(), errorMessage ) );
491 conversionError =
true;
494 else if ( attrValue.userType() != originalValue.userType() )
497 it->setAttribute( i, attrValue );
502 if ( conversionError )
511 mFeatures.insert( mNextFeatureId, *it );
512 addedFids.insert( mNextFeatureId );
514 if ( it->hasGeometry() )
517 mExtent.combineExtentWith( it->geometry().boundingBox() );
521 mSpatialIndex->addFeature( *it );
532 mFeatures.remove( addedFid );
535 mNextFeatureId = oldNextFeatureId;
545bool QgsMemoryProvider::deleteFeatures(
const QgsFeatureIds &
id )
547 for ( QgsFeatureIds::const_iterator it =
id.begin(); it !=
id.end(); ++it )
549 const QgsFeatureMap::iterator fit = mFeatures.find( *it );
552 if ( fit == mFeatures.end() )
557 mSpatialIndex->deleteFeature( *fit );
559 mFeatures.erase( fit );
568bool QgsMemoryProvider::addAttributes(
const QList<QgsField> &attributes )
570 bool fieldWasAdded {
false };
573 if ( !supportedType( field ) )
577 bool isNativeTypeName =
false;
578 NativeType nativeTypeCandidate( QString(), QString(), QMetaType::Type::UnknownType );
579 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
580 for (
const NativeType &nativeType : nativeTypesList )
582 if ( nativeType.mTypeName.toLower() == field.
typeName().toLower() )
584 isNativeTypeName =
true;
588 if ( nativeType.mType == field.
type()
589 && nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
590 nativeTypeCandidate = nativeType;
592 if ( !isNativeTypeName )
594 if ( nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
600 field.
setTypeName( nativeTypeCandidate.mTypeName );
604 mFields.append( field );
605 fieldWasAdded =
true;
607 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
611 attr.append( QVariant() );
615 return fieldWasAdded;
618bool QgsMemoryProvider::renameAttributes(
const QgsFieldNameMap &renamedAttributes )
620 QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
622 for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
624 const int fieldIndex = renameIt.key();
625 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
630 if ( mFields.indexFromName( renameIt.value() ) >= 0 )
637 mFields.rename( fieldIndex, renameIt.value() );
642bool QgsMemoryProvider::deleteAttributes(
const QgsAttributeIds &attributes )
644 QList<int> attrIdx( attributes.begin(), attributes.end() );
645 std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
648 for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
651 mFields.remove( idx );
653 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
667 bool result {
true };
671 QString errorMessage;
672 for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
674 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
675 if ( fit == mFeatures.end() )
682 for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
684 const int fieldIndex = it2.key();
685 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
688 QVariant attrValue = it2.value();
694 && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
695 if ( conversionError )
700 pushError( tr(
"Could not change attribute %1 having type %2 for feature %4: %3" )
701 .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
702 errorMessage ).arg( it.key() ) );
707 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
708 fit->setAttribute( it2.key(), attrValue );
710 rollBackMap.insert( it.key(), rollBackAttrs );
716 changeAttributeValues( rollBackMap );
725bool QgsMemoryProvider::changeGeometryValues(
const QgsGeometryMap &geometry_map )
727 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
729 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
730 if ( fit == mFeatures.end() )
735 mSpatialIndex->deleteFeature( *fit );
737 fit->setGeometry( it.value() );
741 mSpatialIndex->addFeature( *fit );
749QString QgsMemoryProvider::subsetString()
const
751 return mSubsetString;
754bool QgsMemoryProvider::setSubsetString(
const QString &theSQL,
bool updateFeatureCount )
756 Q_UNUSED( updateFeatureCount )
758 if ( !theSQL.isEmpty() )
761 if ( tempExpression.hasParserError() )
765 if ( theSQL == mSubsetString )
768 mSubsetString = theSQL;
776bool QgsMemoryProvider::supportsSubsetString()
const
781QString QgsMemoryProvider::subsetStringDialect()
const
783 return tr(
"QGIS expression" );
786QString QgsMemoryProvider::subsetStringHelpUrl()
const
792bool QgsMemoryProvider::createSpatialIndex()
794 if ( !mSpatialIndex )
796 mSpatialIndex = std::make_unique<QgsSpatialIndex>();
799 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
801 mSpatialIndex->addFeature( *it );
824bool QgsMemoryProvider::truncate()
832void QgsMemoryProvider::updateExtents()
837QString QgsMemoryProvider::name()
const
839 return TEXT_PROVIDER_KEY;
842QString QgsMemoryProvider::description()
const
844 return TEXT_PROVIDER_DESCRIPTION;
848QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
849 :
QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
853QIcon QgsMemoryProviderMetadata::icon()
const
860 return new QgsMemoryProvider( uri, options, flags );
863QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes()
const
868#undef TEXT_PROVIDER_KEY
869#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.