29#include <QRegularExpression>
33#include "moc_qgsmemoryprovider.cpp"
37#define TEXT_PROVIDER_KEY QStringLiteral( "memory" )
38#define TEXT_PROVIDER_DESCRIPTION QStringLiteral( "Memory provider" )
45 const QUrl url = QUrl::fromEncoded( uri.toUtf8() );
46 const QUrlQuery query( url );
48 if ( query.hasQueryItem( QStringLiteral(
"geometry" ) ) )
50 geometry = query.queryItemValue( QStringLiteral(
"geometry" ) );
54 geometry = url.path();
57 if ( geometry.compare( QLatin1String(
"none" ), Qt::CaseInsensitive ) == 0 )
66 if ( query.hasQueryItem( QStringLiteral(
"crs" ) ) )
68 const QString crsDef = query.queryItemValue( QStringLiteral(
"crs" ) );
69 mCrs.createFromString( crsDef );
80 setNativeTypes( QList< NativeType >()
127 if ( query.hasQueryItem( QStringLiteral(
"field" ) ) )
129 QList<QgsField> attributes;
130 const thread_local QRegularExpression reFieldDef(
"\\:"
136 QRegularExpression::CaseInsensitiveOption );
137 const QStringList fields = query.allQueryItemValues( QStringLiteral(
"field" ) );
138 for (
int i = 0; i < fields.size(); i++ )
140 QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
141 const QRegularExpressionMatch regularExpressionMatch = reFieldDef.match( name );
144 QMetaType::Type type = QMetaType::Type::QString;
145 QMetaType::Type subType = QMetaType::Type::UnknownType;
146 QString typeName( QStringLiteral(
"string" ) );
150 if ( regularExpressionMatch.hasMatch() )
152 name = name.mid( 0, regularExpressionMatch.capturedStart() );
153 typeName = regularExpressionMatch.captured( 1 ).toLower();
156 bool isNativeType =
false;
157 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
158 for (
const NativeType &nativeType : nativeTypesList )
160 if ( nativeType.mTypeName.toLower() == typeName )
163 type = nativeType.mType;
164 subType = nativeType.mSubType;
165 typeName = nativeType.mTypeName;
171 if ( isNativeType ==
false )
173 if ( typeName == QLatin1String(
"int" ) )
175 type = QMetaType::Type::Int;
176 typeName = QStringLiteral(
"integer" );
178 else if ( typeName == QLatin1String(
"long" ) )
180 type = QMetaType::Type::LongLong;
181 typeName = QStringLiteral(
"int8" );
183 else if ( typeName == QLatin1String(
"bool" ) )
185 type = QMetaType::Type::Bool;
186 typeName = QStringLiteral(
"boolean" );
190 QgsLogger::warning( tr(
"Unsupported typeName '%1'. Will be handled as string." ).arg( typeName ) );
191 type = QMetaType::Type::QString;
192 typeName = QStringLiteral(
"string" );
197 if ( typeName == QLatin1String(
"real" ) || typeName == QLatin1String(
"double" ) )
203 if ( !regularExpressionMatch.captured( 2 ).isEmpty() )
204 length = regularExpressionMatch.captured( 2 ).toInt();
206 if ( !regularExpressionMatch.captured( 3 ).isEmpty() )
207 precision = regularExpressionMatch.captured( 3 ).toInt();
210 if ( !regularExpressionMatch.captured( 4 ).isEmpty() )
212 if ( subType == QMetaType::Type::UnknownType )
215 if ( type != QMetaType::Type::QVariantList && type != QMetaType::Type::QStringList )
216 type = type == QMetaType::Type::QString ? QMetaType::Type::QStringList : QMetaType::Type::QVariantList;
218 const QLatin1String listSuffix(
"list" );
219 if ( !typeName.endsWith( listSuffix ) )
220 typeName += QLatin1String(
"list" );
224 attributes.append(
QgsField( name, type, typeName, length, precision, QString(), subType ) );
226 addAttributes( attributes );
229 if ( query.hasQueryItem( QStringLiteral(
"index" ) ) && query.queryItemValue( QStringLiteral(
"index" ) ) == QLatin1String(
"yes" ) )
231 createSpatialIndex();
236QgsMemoryProvider::~QgsMemoryProvider()
241QString QgsMemoryProvider::providerKey()
243 return TEXT_PROVIDER_KEY;
246QString QgsMemoryProvider::providerDescription()
248 return TEXT_PROVIDER_DESCRIPTION;
253 return new QgsMemoryFeatureSource(
this );
256QString QgsMemoryProvider::dataSourceUri(
bool expandAuthConfig )
const
258 Q_UNUSED( expandAuthConfig )
260 QUrl uri( QStringLiteral(
"memory" ) );
263 query.addQueryItem( QStringLiteral(
"geometry" ), geometry );
265 if ( mCrs.isValid() )
268 const QString authid = mCrs.authid();
269 if ( authid.startsWith( QLatin1String(
"EPSG:" ) ) )
277 query.addQueryItem( QStringLiteral(
"crs" ), crsDef );
281 query.addQueryItem( QStringLiteral(
"index" ), QStringLiteral(
"yes" ) );
284 QgsAttributeList attrs =
const_cast<QgsMemoryProvider *
>( this )->attributeIndexes();
285 for (
int i = 0; i < attrs.size(); i++ )
287 const QgsField field = mFields.at( attrs[i] );
288 QString fieldDef = field.
name();
290 QString typeName = field.
typeName();
292 if ( field.
type() == QMetaType::Type::QVariantList || field.
type() == QMetaType::Type::QStringList )
296 case QMetaType::Type::Int:
297 typeName = QStringLiteral(
"integer" );
300 case QMetaType::Type::LongLong:
301 typeName = QStringLiteral(
"long" );
304 case QMetaType::Type::Double:
305 typeName = QStringLiteral(
"double" );
308 case QMetaType::Type::QString:
309 typeName = QStringLiteral(
"string" );
318 fieldDef.append( QStringLiteral(
":%2(%3,%4)%5" ).arg( typeName ).arg( field.
length() ).arg( field.
precision() ).arg( isList ? QStringLiteral(
"[]" ) : QString() ) );
319 query.addQueryItem( QStringLiteral(
"field" ), fieldDef );
321 uri.setQuery( query );
323 return QString( uri.toEncoded() );
327QString QgsMemoryProvider::storageType()
const
329 return QStringLiteral(
"Memory storage" );
334 return QgsFeatureIterator(
new QgsMemoryFeatureIterator(
new QgsMemoryFeatureSource(
this ),
true, request ) );
340 if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
343 if ( mSubsetString.isEmpty() )
346 const auto constMFeatures = mFeatures;
347 for (
const QgsFeature &feat : constMFeatures )
349 if ( feat.hasGeometry() )
350 mExtent.combineExtentWith( feat.geometry().boundingBox() );
364 else if ( mFeatures.isEmpty() )
377long long QgsMemoryProvider::featureCount()
const
379 if ( mSubsetString.isEmpty() )
380 return mFeatures.count();
393QgsFields QgsMemoryProvider::fields()
const
398bool QgsMemoryProvider::isValid()
const
411 if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
414 mFeatures = other->mFeatures;
415 mNextFeatureId = other->mNextFeatureId;
416 mExtent = other->mExtent;
421bool QgsMemoryProvider::addFeatures(
QgsFeatureList &flist, Flags flags )
425 const bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
427 const int fieldCount = mFields.count();
430 const auto oldExtent { mExtent };
431 const auto oldNextFeatureId { mNextFeatureId };
434 for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result ; ++it )
436 it->setId( mNextFeatureId );
437 it->setValid(
true );
438 const int attributeCount = it->attributeCount();
439 if ( attributeCount < fieldCount )
444 for (
int i = attributeCount; i < mFields.count(); ++i )
448 it->setAttributes( attributes );
450 else if ( attributeCount > fieldCount )
453 pushError( tr(
"Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( attributeCount ) );
455 attributes.resize( mFields.count() );
456 it->setAttributes( attributes );
466 pushError( tr(
"Could not add feature with geometry type %1 to layer of type %2" ).arg(
QgsWkbTypes::displayString( it->geometry().wkbType() ),
473 bool conversionError {
false };
474 QString errorMessage;
475 for (
int i = 0; i < mFields.count(); ++i )
477 const QVariant originalValue = it->attribute( i );
478 QVariant attrValue = originalValue;
479 if ( !
QgsVariantUtils::isNull( attrValue ) && ! mFields.at( i ).convertCompatible( attrValue, &errorMessage ) )
484 pushError( tr(
"Could not store attribute \"%1\": %2" )
485 .arg( mFields.at( i ).name(), errorMessage ) );
488 conversionError =
true;
491 else if ( attrValue.userType() != originalValue.userType() )
494 it->setAttribute( i, attrValue );
499 if ( conversionError )
508 mFeatures.insert( mNextFeatureId, *it );
509 addedFids.insert( mNextFeatureId );
511 if ( it->hasGeometry() )
514 mExtent.combineExtentWith( it->geometry().boundingBox() );
518 mSpatialIndex->addFeature( *it );
529 mFeatures.remove( addedFid );
532 mNextFeatureId = oldNextFeatureId;
542bool QgsMemoryProvider::deleteFeatures(
const QgsFeatureIds &
id )
544 for ( QgsFeatureIds::const_iterator it =
id.begin(); it !=
id.end(); ++it )
546 const QgsFeatureMap::iterator fit = mFeatures.find( *it );
549 if ( fit == mFeatures.end() )
554 mSpatialIndex->deleteFeature( *fit );
556 mFeatures.erase( fit );
565bool QgsMemoryProvider::addAttributes(
const QList<QgsField> &attributes )
567 bool fieldWasAdded {
false };
570 if ( !supportedType( field ) )
574 bool isNativeTypeName =
false;
575 NativeType nativeTypeCandidate( QString(), QString(), QMetaType::Type::UnknownType );
576 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
577 for (
const NativeType &nativeType : nativeTypesList )
579 if ( nativeType.mTypeName.toLower() == field.
typeName().toLower() )
581 isNativeTypeName =
true;
585 if ( nativeType.mType == field.
type()
586 && nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
587 nativeTypeCandidate = nativeType;
589 if ( !isNativeTypeName )
591 if ( nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
597 field.
setTypeName( nativeTypeCandidate.mTypeName );
601 mFields.append( field );
602 fieldWasAdded =
true;
604 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
608 attr.append( QVariant() );
612 return fieldWasAdded;
615bool QgsMemoryProvider::renameAttributes(
const QgsFieldNameMap &renamedAttributes )
617 QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
619 for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
621 const int fieldIndex = renameIt.key();
622 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
627 if ( mFields.indexFromName( renameIt.value() ) >= 0 )
634 mFields.rename( fieldIndex, renameIt.value() );
639bool QgsMemoryProvider::deleteAttributes(
const QgsAttributeIds &attributes )
641 QList<int> attrIdx( attributes.begin(), attributes.end() );
642 std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
645 for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
648 mFields.remove( idx );
650 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
664 bool result {
true };
668 QString errorMessage;
669 for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
671 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
672 if ( fit == mFeatures.end() )
679 for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
681 const int fieldIndex = it2.key();
682 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
685 QVariant attrValue = it2.value();
691 && ! 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" )
698 .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
699 errorMessage ).arg( it.key() ) );
704 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
705 fit->setAttribute( it2.key(), attrValue );
707 rollBackMap.insert( it.key(), rollBackAttrs );
713 changeAttributeValues( rollBackMap );
722bool QgsMemoryProvider::changeGeometryValues(
const QgsGeometryMap &geometry_map )
724 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
726 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
727 if ( fit == mFeatures.end() )
732 mSpatialIndex->deleteFeature( *fit );
734 fit->setGeometry( it.value() );
738 mSpatialIndex->addFeature( *fit );
746QString QgsMemoryProvider::subsetString()
const
748 return mSubsetString;
751bool QgsMemoryProvider::setSubsetString(
const QString &theSQL,
bool updateFeatureCount )
753 Q_UNUSED( updateFeatureCount )
755 if ( !theSQL.isEmpty() )
758 if ( tempExpression.hasParserError() )
762 if ( theSQL == mSubsetString )
765 mSubsetString = theSQL;
773bool QgsMemoryProvider::supportsSubsetString()
const
778QString QgsMemoryProvider::subsetStringDialect()
const
780 return tr(
"QGIS expression" );
783QString QgsMemoryProvider::subsetStringHelpUrl()
const
789bool QgsMemoryProvider::createSpatialIndex()
791 if ( !mSpatialIndex )
793 mSpatialIndex = std::make_unique<QgsSpatialIndex>();
796 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
798 mSpatialIndex->addFeature( *it );
821bool QgsMemoryProvider::truncate()
829void QgsMemoryProvider::updateExtents()
834QString QgsMemoryProvider::name()
const
836 return TEXT_PROVIDER_KEY;
839QString QgsMemoryProvider::description()
const
841 return TEXT_PROVIDER_DESCRIPTION;
845QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
846 :
QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
850QIcon QgsMemoryProviderMetadata::icon()
const
857 return new QgsMemoryProvider( uri, options, flags );
860QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes()
const
865#undef TEXT_PROVIDER_KEY
866#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.