30#include <QRegularExpression>
35#define TEXT_PROVIDER_KEY QStringLiteral( "memory" )
36#define TEXT_PROVIDER_DESCRIPTION QStringLiteral( "Memory provider" )
38QgsMemoryProvider::QgsMemoryProvider(
const QString &uri,
const ProviderOptions &options, QgsDataProvider::ReadFlags flags )
43 const QUrl url = QUrl::fromEncoded( uri.toUtf8() );
44 const QUrlQuery query( url );
46 if ( query.hasQueryItem( QStringLiteral(
"geometry" ) ) )
48 geometry = query.queryItemValue( QStringLiteral(
"geometry" ) );
52 geometry = url.path();
55 if ( geometry.compare( QLatin1String(
"none" ), Qt::CaseInsensitive ) == 0 )
64 if ( query.hasQueryItem( QStringLiteral(
"crs" ) ) )
66 const QString crsDef = query.queryItemValue( QStringLiteral(
"crs" ) );
67 mCrs.createFromString( crsDef );
78 setNativeTypes( QList< NativeType >()
124 if ( query.hasQueryItem( QStringLiteral(
"field" ) ) )
126 QList<QgsField> attributes;
127 const thread_local QRegularExpression reFieldDef(
"\\:"
133 QRegularExpression::CaseInsensitiveOption );
134 const QStringList fields = query.allQueryItemValues( QStringLiteral(
"field" ) );
135 for (
int i = 0; i < fields.size(); i++ )
137 QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
138 const QRegularExpressionMatch regularExpressionMatch = reFieldDef.match( name );
141 QVariant::Type type = QVariant::String;
142 QVariant::Type subType = QVariant::Invalid;
143 QString
typeName( QStringLiteral(
"string" ) );
147 if ( regularExpressionMatch.hasMatch() )
149 name = name.mid( 0, regularExpressionMatch.capturedStart() );
150 typeName = regularExpressionMatch.captured( 1 ).toLower();
153 bool isNativeType =
false;
154 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
155 for (
const NativeType &nativeType : nativeTypesList )
157 if ( nativeType.mTypeName.toLower() ==
typeName )
160 type = nativeType.mType;
161 subType = nativeType.mSubType;
168 if ( isNativeType ==
false )
170 if (
typeName == QLatin1String(
"int" ) )
172 type = QVariant::Int;
173 typeName = QStringLiteral(
"integer" );
175 else if (
typeName == QLatin1String(
"long" ) )
177 type = QVariant::LongLong;
178 typeName = QStringLiteral(
"int8" );
180 else if (
typeName == QLatin1String(
"bool" ) )
182 type = QVariant::Bool;
183 typeName = QStringLiteral(
"boolean" );
188 type = QVariant::String;
189 typeName = QStringLiteral(
"string" );
194 if (
typeName == QLatin1String(
"real" ) ||
typeName == QLatin1String(
"double" ) )
200 if ( !regularExpressionMatch.captured( 2 ).isEmpty() )
201 length = regularExpressionMatch.captured( 2 ).toInt();
203 if ( !regularExpressionMatch.captured( 3 ).isEmpty() )
204 precision = regularExpressionMatch.captured( 3 ).toInt();
207 if ( !regularExpressionMatch.captured( 4 ).isEmpty() )
209 if ( subType == QVariant::Invalid )
212 if ( type != QVariant::List && type != QVariant::StringList )
213 type = type == QVariant::String ? QVariant::StringList : QVariant::List;
215 const QLatin1String listSuffix(
"list" );
216 if ( !
typeName.endsWith( listSuffix ) )
217 typeName += QLatin1String(
"list" );
223 addAttributes( attributes );
226 if ( query.hasQueryItem( QStringLiteral(
"index" ) ) && query.queryItemValue( QStringLiteral(
"index" ) ) == QLatin1String(
"yes" ) )
228 createSpatialIndex();
233QgsMemoryProvider::~QgsMemoryProvider()
235 delete mSpatialIndex;
238QString QgsMemoryProvider::providerKey()
240 return TEXT_PROVIDER_KEY;
243QString QgsMemoryProvider::providerDescription()
245 return TEXT_PROVIDER_DESCRIPTION;
250 return new QgsMemoryFeatureSource(
this );
253QString QgsMemoryProvider::dataSourceUri(
bool expandAuthConfig )
const
255 Q_UNUSED( expandAuthConfig )
257 QUrl uri( QStringLiteral(
"memory" ) );
260 query.addQueryItem( QStringLiteral(
"geometry" ), geometry );
262 if ( mCrs.isValid() )
265 const QString authid = mCrs.authid();
266 if ( authid.startsWith( QLatin1String(
"EPSG:" ) ) )
274 query.addQueryItem( QStringLiteral(
"crs" ), crsDef );
278 query.addQueryItem( QStringLiteral(
"index" ), QStringLiteral(
"yes" ) );
282 for (
int i = 0; i < attrs.size(); i++ )
294 typeName = QStringLiteral(
"integer" );
297 case QVariant::LongLong:
298 typeName = QStringLiteral(
"long" );
301 case QVariant::Double:
302 typeName = QStringLiteral(
"double" );
305 case QVariant::String:
306 typeName = QStringLiteral(
"string" );
315 fieldDef.append( QStringLiteral(
":%2(%3,%4)%5" ).arg(
typeName ).arg(
field.
length() ).arg(
field.
precision() ).arg( isList ? QStringLiteral(
"[]" ) : QString() ) );
316 query.addQueryItem( QStringLiteral(
"field" ), fieldDef );
318 uri.setQuery( query );
320 return QString( uri.toEncoded() );
324QString QgsMemoryProvider::storageType()
const
326 return QStringLiteral(
"Memory storage" );
331 return QgsFeatureIterator(
new QgsMemoryFeatureIterator(
new QgsMemoryFeatureSource(
this ),
true, request ) );
337 if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
340 if ( mSubsetString.isEmpty() )
343 const auto constMFeatures = mFeatures;
344 for (
const QgsFeature &feat : constMFeatures )
346 if ( feat.hasGeometry() )
347 mExtent.combineExtentWith( feat.geometry().boundingBox() );
361 else if ( mFeatures.isEmpty() )
363 mExtent.setMinimal();
374long long QgsMemoryProvider::featureCount()
const
376 if ( mSubsetString.isEmpty() )
377 return mFeatures.count();
390QgsFields QgsMemoryProvider::fields()
const
395bool QgsMemoryProvider::isValid()
const
408 if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
411 mFeatures = other->mFeatures;
412 mNextFeatureId = other->mNextFeatureId;
413 mExtent = other->mExtent;
418bool QgsMemoryProvider::addFeatures(
QgsFeatureList &flist, Flags flags )
422 const bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
424 const int fieldCount = mFields.count();
427 const auto oldExtent { mExtent };
428 const auto oldNextFeatureId { mNextFeatureId };
431 for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result ; ++it )
433 it->setId( mNextFeatureId );
434 it->setValid(
true );
435 if ( it->attributes().count() < fieldCount )
440 for (
int i = it->attributes().count(); i < mFields.count(); ++i )
442 attributes.append( QVariant( mFields.at( i ).type() ) );
444 it->setAttributes( attributes );
446 else if ( it->attributes().count() > fieldCount )
449 pushError( tr(
"Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( it->attributes().count() ) );
451 attributes.resize( mFields.count() );
452 it->setAttributes( attributes );
462 pushError( tr(
"Could not add feature with geometry type %1 to layer of type %2" ).arg(
QgsWkbTypes::displayString( it->geometry().wkbType() ),
469 bool conversionError {
false };
470 QString errorMessage;
471 for (
int i = 0; i < mFields.count(); ++i )
473 const QVariant originalValue = it->attribute( i );
474 QVariant attrValue = originalValue;
475 if ( !
QgsVariantUtils::isNull( attrValue ) && ! mFields.at( i ).convertCompatible( attrValue, &errorMessage ) )
480 pushError( tr(
"Could not store attribute \"%1\": %2" )
481 .arg( mFields.at( i ).name(), errorMessage ) );
484 conversionError =
true;
487 else if ( attrValue.type() != originalValue.type() )
490 it->setAttribute( i, attrValue );
495 if ( conversionError )
497 if ( flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
504 mFeatures.insert( mNextFeatureId, *it );
505 addedFids.insert( mNextFeatureId );
507 if ( it->hasGeometry() )
510 mExtent.combineExtentWith( it->geometry().boundingBox() );
514 mSpatialIndex->addFeature( *it );
521 if ( ! result && flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
525 mFeatures.remove( addedFid );
528 mNextFeatureId = oldNextFeatureId;
538bool QgsMemoryProvider::deleteFeatures(
const QgsFeatureIds &
id )
540 for ( QgsFeatureIds::const_iterator it =
id.begin(); it !=
id.end(); ++it )
542 const QgsFeatureMap::iterator fit = mFeatures.find( *it );
545 if ( fit == mFeatures.end() )
550 mSpatialIndex->deleteFeature( *fit );
552 mFeatures.erase( fit );
561bool QgsMemoryProvider::addAttributes(
const QList<QgsField> &attributes )
563 bool fieldWasAdded {
false };
566 if ( !supportedType(
field ) )
570 bool isNativeTypeName =
false;
571 NativeType nativeTypeCandidate( QString(), QString(), QVariant::Invalid );
572 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
573 for (
const NativeType &nativeType : nativeTypesList )
575 if ( nativeType.mTypeName.toLower() ==
field.
typeName().toLower() )
577 isNativeTypeName =
true;
582 && nativeTypeCandidate.mType == QVariant::Invalid )
583 nativeTypeCandidate = nativeType;
585 if ( !isNativeTypeName )
587 if ( nativeTypeCandidate.mType == QVariant::Invalid )
597 mFields.append(
field );
598 fieldWasAdded =
true;
600 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
604 attr.append( QVariant() );
608 return fieldWasAdded;
611bool QgsMemoryProvider::renameAttributes(
const QgsFieldNameMap &renamedAttributes )
613 QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
615 for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
617 const int fieldIndex = renameIt.key();
618 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
623 if ( mFields.indexFromName( renameIt.value() ) >= 0 )
630 mFields.rename( fieldIndex, renameIt.value() );
635bool QgsMemoryProvider::deleteAttributes(
const QgsAttributeIds &attributes )
637 QList<int> attrIdx( attributes.begin(), attributes.end() );
638 std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
641 for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
644 mFields.remove( idx );
646 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
660 bool result {
true };
664 QString errorMessage;
665 for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
667 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
668 if ( fit == mFeatures.end() )
675 for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
677 QVariant attrValue = it2.value();
680 && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
681 if ( conversionError )
686 pushError( tr(
"Could not change attribute %1 having type %2 for feature %4: %3" )
687 .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
688 errorMessage ).arg( it.key() ) );
693 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
694 fit->setAttribute( it2.key(), attrValue );
696 rollBackMap.insert( it.key(), rollBackAttrs );
702 changeAttributeValues( rollBackMap );
711bool QgsMemoryProvider::changeGeometryValues(
const QgsGeometryMap &geometry_map )
713 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
715 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
716 if ( fit == mFeatures.end() )
721 mSpatialIndex->deleteFeature( *fit );
723 fit->setGeometry( it.value() );
727 mSpatialIndex->addFeature( *fit );
735QString QgsMemoryProvider::subsetString()
const
737 return mSubsetString;
740bool QgsMemoryProvider::setSubsetString(
const QString &theSQL,
bool updateFeatureCount )
742 Q_UNUSED( updateFeatureCount )
744 if ( !theSQL.isEmpty() )
747 if ( tempExpression.hasParserError() )
751 if ( theSQL == mSubsetString )
754 mSubsetString = theSQL;
756 mExtent.setMinimal();
762bool QgsMemoryProvider::createSpatialIndex()
764 if ( !mSpatialIndex )
769 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
771 mSpatialIndex->addFeature( *it );
779 return mSpatialIndex ? SpatialIndexPresent : SpatialIndexNotPresent;
782QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities()
const
784 return AddFeatures | DeleteFeatures | ChangeGeometries |
785 ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
786 SelectAtId | CircularGeometries | FastTruncate;
789bool QgsMemoryProvider::truncate()
793 mExtent.setMinimal();
797void QgsMemoryProvider::updateExtents()
799 mExtent.setMinimal();
802QString QgsMemoryProvider::name()
const
804 return TEXT_PROVIDER_KEY;
807QString QgsMemoryProvider::description()
const
809 return TEXT_PROVIDER_DESCRIPTION;
813QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
814 :
QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
818QIcon QgsMemoryProviderMetadata::icon()
const
825 return new QgsMemoryProvider( uri, options, flags );
828QList<QgsMapLayerType> QgsMemoryProviderMetadata::supportedLayerTypes()
const
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.
This class represents a coordinate reference system (CRS).
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
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)
This class wraps a request for features to a vector layer (or directly its vector data provider).
SpatialIndexPresence
Enumeration of spatial index presence states.
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.
QVariant::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.
void setMinimal() SIP_HOLDGIL
Set a rectangle so that min corner is at max and max corner is at min.
A spatial index for QgsFeature objects.
static QString typeToDisplayString(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns a user-friendly translated string representing a QVariant type.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
This is the base class for vector data providers.
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Type parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
Type
The WKB type describes the number of dimensions a geometry has.
static QString displayString(Type type) SIP_HOLDGIL
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
@ VectorLayer
Vector layer.
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
const QgsCoordinateReferenceSystem & crs
const QgsAttributeList & attributeIndexes
Setting options for creating vector data providers.