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 >()
125 if ( query.hasQueryItem( QStringLiteral(
"field" ) ) )
127 QList<QgsField> attributes;
128 const thread_local QRegularExpression reFieldDef(
"\\:"
134 QRegularExpression::CaseInsensitiveOption );
135 const QStringList fields = query.allQueryItemValues( QStringLiteral(
"field" ) );
136 for (
int i = 0; i < fields.size(); i++ )
138 QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
139 const QRegularExpressionMatch regularExpressionMatch = reFieldDef.match( name );
142 QVariant::Type type = QVariant::String;
143 QVariant::Type subType = QVariant::Invalid;
144 QString
typeName( QStringLiteral(
"string" ) );
148 if ( regularExpressionMatch.hasMatch() )
150 name = name.mid( 0, regularExpressionMatch.capturedStart() );
151 typeName = regularExpressionMatch.captured( 1 ).toLower();
154 bool isNativeType =
false;
155 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
156 for (
const NativeType &nativeType : nativeTypesList )
158 if ( nativeType.mTypeName.toLower() ==
typeName )
161 type = nativeType.mType;
162 subType = nativeType.mSubType;
169 if ( isNativeType ==
false )
171 if (
typeName == QLatin1String(
"int" ) )
173 type = QVariant::Int;
174 typeName = QStringLiteral(
"integer" );
176 else if (
typeName == QLatin1String(
"long" ) )
178 type = QVariant::LongLong;
179 typeName = QStringLiteral(
"int8" );
181 else if (
typeName == QLatin1String(
"bool" ) )
183 type = QVariant::Bool;
184 typeName = QStringLiteral(
"boolean" );
189 type = QVariant::String;
190 typeName = QStringLiteral(
"string" );
195 if (
typeName == QLatin1String(
"real" ) ||
typeName == QLatin1String(
"double" ) )
201 if ( !regularExpressionMatch.captured( 2 ).isEmpty() )
202 length = regularExpressionMatch.captured( 2 ).toInt();
204 if ( !regularExpressionMatch.captured( 3 ).isEmpty() )
205 precision = regularExpressionMatch.captured( 3 ).toInt();
208 if ( !regularExpressionMatch.captured( 4 ).isEmpty() )
210 if ( subType == QVariant::Invalid )
213 if ( type != QVariant::List && type != QVariant::StringList )
214 type = type == QVariant::String ? QVariant::StringList : QVariant::List;
216 const QLatin1String listSuffix(
"list" );
217 if ( !
typeName.endsWith( listSuffix ) )
218 typeName += QLatin1String(
"list" );
224 addAttributes( attributes );
227 if ( query.hasQueryItem( QStringLiteral(
"index" ) ) && query.queryItemValue( QStringLiteral(
"index" ) ) == QLatin1String(
"yes" ) )
229 createSpatialIndex();
234QgsMemoryProvider::~QgsMemoryProvider()
236 delete mSpatialIndex;
239QString QgsMemoryProvider::providerKey()
241 return TEXT_PROVIDER_KEY;
244QString QgsMemoryProvider::providerDescription()
246 return TEXT_PROVIDER_DESCRIPTION;
251 return new QgsMemoryFeatureSource(
this );
254QString QgsMemoryProvider::dataSourceUri(
bool expandAuthConfig )
const
256 Q_UNUSED( expandAuthConfig )
258 QUrl uri( QStringLiteral(
"memory" ) );
261 query.addQueryItem( QStringLiteral(
"geometry" ), geometry );
263 if ( mCrs.isValid() )
266 const QString authid = mCrs.authid();
267 if ( authid.startsWith( QLatin1String(
"EPSG:" ) ) )
275 query.addQueryItem( QStringLiteral(
"crs" ), crsDef );
279 query.addQueryItem( QStringLiteral(
"index" ), QStringLiteral(
"yes" ) );
283 for (
int i = 0; i < attrs.size(); i++ )
285 const QgsField field = mFields.at( attrs[i] );
286 QString fieldDef = field.
name();
290 if ( field.
type() == QVariant::List || field.
type() == QVariant::StringList )
295 typeName = QStringLiteral(
"integer" );
298 case QVariant::LongLong:
299 typeName = QStringLiteral(
"long" );
302 case QVariant::Double:
303 typeName = QStringLiteral(
"double" );
306 case QVariant::String:
307 typeName = QStringLiteral(
"string" );
316 fieldDef.append( QStringLiteral(
":%2(%3,%4)%5" ).arg(
typeName ).arg( field.
length() ).arg( field.
precision() ).arg( isList ? QStringLiteral(
"[]" ) : QString() ) );
317 query.addQueryItem( QStringLiteral(
"field" ), fieldDef );
319 uri.setQuery( query );
321 return QString( uri.toEncoded() );
325QString QgsMemoryProvider::storageType()
const
327 return QStringLiteral(
"Memory storage" );
332 return QgsFeatureIterator(
new QgsMemoryFeatureIterator(
new QgsMemoryFeatureSource(
this ),
true, request ) );
338 if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
341 if ( mSubsetString.isEmpty() )
344 const auto constMFeatures = mFeatures;
345 for (
const QgsFeature &feat : constMFeatures )
347 if ( feat.hasGeometry() )
348 mExtent.combineExtentWith( feat.geometry().boundingBox() );
362 else if ( mFeatures.isEmpty() )
375long long QgsMemoryProvider::featureCount()
const
377 if ( mSubsetString.isEmpty() )
378 return mFeatures.count();
391QgsFields QgsMemoryProvider::fields()
const
396bool QgsMemoryProvider::isValid()
const
409 if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
412 mFeatures = other->mFeatures;
413 mNextFeatureId = other->mNextFeatureId;
414 mExtent = other->mExtent;
419bool QgsMemoryProvider::addFeatures(
QgsFeatureList &flist, Flags flags )
423 const bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
425 const int fieldCount = mFields.count();
428 const auto oldExtent { mExtent };
429 const auto oldNextFeatureId { mNextFeatureId };
432 for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result ; ++it )
434 it->setId( mNextFeatureId );
435 it->setValid(
true );
436 if ( it->attributes().count() < fieldCount )
441 for (
int i = it->attributes().count(); i < mFields.count(); ++i )
443 attributes.append( QVariant( mFields.at( i ).type() ) );
445 it->setAttributes( attributes );
447 else if ( it->attributes().count() > fieldCount )
450 pushError( tr(
"Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( it->attributes().count() ) );
452 attributes.resize( mFields.count() );
453 it->setAttributes( attributes );
463 pushError( tr(
"Could not add feature with geometry type %1 to layer of type %2" ).arg(
QgsWkbTypes::displayString( it->geometry().wkbType() ),
470 bool conversionError {
false };
471 QString errorMessage;
472 for (
int i = 0; i < mFields.count(); ++i )
474 const QVariant originalValue = it->attribute( i );
475 QVariant attrValue = originalValue;
476 if ( !
QgsVariantUtils::isNull( attrValue ) && ! mFields.at( i ).convertCompatible( attrValue, &errorMessage ) )
481 pushError( tr(
"Could not store attribute \"%1\": %2" )
482 .arg( mFields.at( i ).name(), errorMessage ) );
485 conversionError =
true;
488 else if ( attrValue.type() != originalValue.type() )
491 it->setAttribute( i, attrValue );
496 if ( conversionError )
505 mFeatures.insert( mNextFeatureId, *it );
506 addedFids.insert( mNextFeatureId );
508 if ( it->hasGeometry() )
511 mExtent.combineExtentWith( it->geometry().boundingBox() );
515 mSpatialIndex->addFeature( *it );
526 mFeatures.remove( addedFid );
529 mNextFeatureId = oldNextFeatureId;
539bool QgsMemoryProvider::deleteFeatures(
const QgsFeatureIds &
id )
541 for ( QgsFeatureIds::const_iterator it =
id.begin(); it !=
id.end(); ++it )
543 const QgsFeatureMap::iterator fit = mFeatures.find( *it );
546 if ( fit == mFeatures.end() )
551 mSpatialIndex->deleteFeature( *fit );
553 mFeatures.erase( fit );
562bool QgsMemoryProvider::addAttributes(
const QList<QgsField> &attributes )
564 bool fieldWasAdded {
false };
567 if ( !supportedType( field ) )
571 bool isNativeTypeName =
false;
572 NativeType nativeTypeCandidate( QString(), QString(), QVariant::Invalid );
573 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
574 for (
const NativeType &nativeType : nativeTypesList )
576 if ( nativeType.mTypeName.toLower() == field.
typeName().toLower() )
578 isNativeTypeName =
true;
582 if ( nativeType.mType == field.
type()
583 && nativeTypeCandidate.mType == QVariant::Invalid )
584 nativeTypeCandidate = nativeType;
586 if ( !isNativeTypeName )
588 if ( nativeTypeCandidate.mType == QVariant::Invalid )
594 field.
setTypeName( nativeTypeCandidate.mTypeName );
598 mFields.append( field );
599 fieldWasAdded =
true;
601 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
605 attr.append( QVariant() );
609 return fieldWasAdded;
612bool QgsMemoryProvider::renameAttributes(
const QgsFieldNameMap &renamedAttributes )
614 QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
616 for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
618 const int fieldIndex = renameIt.key();
619 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
624 if ( mFields.indexFromName( renameIt.value() ) >= 0 )
631 mFields.rename( fieldIndex, renameIt.value() );
636bool QgsMemoryProvider::deleteAttributes(
const QgsAttributeIds &attributes )
638 QList<int> attrIdx( attributes.begin(), attributes.end() );
639 std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
642 for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
645 mFields.remove( idx );
647 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
661 bool result {
true };
665 QString errorMessage;
666 for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
668 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
669 if ( fit == mFeatures.end() )
676 for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
678 const int fieldIndex = it2.key();
679 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
682 QVariant attrValue = it2.value();
685 && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
686 if ( conversionError )
691 pushError( tr(
"Could not change attribute %1 having type %2 for feature %4: %3" )
692 .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
693 errorMessage ).arg( it.key() ) );
698 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
699 fit->setAttribute( it2.key(), attrValue );
701 rollBackMap.insert( it.key(), rollBackAttrs );
707 changeAttributeValues( rollBackMap );
716bool QgsMemoryProvider::changeGeometryValues(
const QgsGeometryMap &geometry_map )
718 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
720 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
721 if ( fit == mFeatures.end() )
726 mSpatialIndex->deleteFeature( *fit );
728 fit->setGeometry( it.value() );
732 mSpatialIndex->addFeature( *fit );
740QString QgsMemoryProvider::subsetString()
const
742 return mSubsetString;
745bool QgsMemoryProvider::setSubsetString(
const QString &theSQL,
bool updateFeatureCount )
747 Q_UNUSED( updateFeatureCount )
749 if ( !theSQL.isEmpty() )
752 if ( tempExpression.hasParserError() )
756 if ( theSQL == mSubsetString )
759 mSubsetString = theSQL;
767bool QgsMemoryProvider::createSpatialIndex()
769 if ( !mSpatialIndex )
774 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
776 mSpatialIndex->addFeature( *it );
784 return mSpatialIndex ? SpatialIndexPresent : SpatialIndexNotPresent;
787QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities()
const
789 return AddFeatures | DeleteFeatures | ChangeGeometries |
790 ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
791 SelectAtId | CircularGeometries | FastTruncate;
794bool QgsMemoryProvider::truncate()
802void QgsMemoryProvider::updateExtents()
807QString QgsMemoryProvider::name()
const
809 return TEXT_PROVIDER_KEY;
812QString QgsMemoryProvider::description()
const
814 return TEXT_PROVIDER_DESCRIPTION;
818QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
819 :
QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
823QIcon QgsMemoryProviderMetadata::icon()
const
830 return new QgsMemoryProvider( uri, options, flags );
833QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes()
const
WkbType
The WKB type describes the number of dimensions a geometry has.
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).
@ RollBackOnErrors
Roll back the whole transaction if a single add feature operation fails.
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 setNull()
Mark a rectangle as being null (holding no spatial information).
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 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 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
const QgsAttributeList & attributeIndexes
Setting options for creating vector data providers.