30#include <QRegularExpression>
35#define TEXT_PROVIDER_KEY QStringLiteral( "memory" )
36#define TEXT_PROVIDER_DESCRIPTION QStringLiteral( "Memory provider" )
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 QMetaType::Type type = QMetaType::Type::QString;
143 QMetaType::Type subType = QMetaType::Type::UnknownType;
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 = QMetaType::Type::Int;
174 typeName = QStringLiteral(
"integer" );
176 else if (
typeName == QLatin1String(
"long" ) )
178 type = QMetaType::Type::LongLong;
179 typeName = QStringLiteral(
"int8" );
181 else if (
typeName == QLatin1String(
"bool" ) )
183 type = QMetaType::Type::Bool;
184 typeName = QStringLiteral(
"boolean" );
189 type = QMetaType::Type::QString;
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 == QMetaType::Type::UnknownType )
213 if ( type != QMetaType::Type::QVariantList && type != QMetaType::Type::QStringList )
214 type = type == QMetaType::Type::QString ? QMetaType::Type::QStringList : QMetaType::Type::QVariantList;
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() == QMetaType::Type::QVariantList || field.
type() == QMetaType::Type::QStringList )
294 case QMetaType::Type::Int:
295 typeName = QStringLiteral(
"integer" );
298 case QMetaType::Type::LongLong:
299 typeName = QStringLiteral(
"long" );
302 case QMetaType::Type::Double:
303 typeName = QStringLiteral(
"double" );
306 case QMetaType::Type::QString:
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 const int attributeCount = it->attributeCount();
437 if ( attributeCount < fieldCount )
442 for (
int i = attributeCount; i < mFields.count(); ++i )
446 it->setAttributes( attributes );
448 else if ( attributeCount > fieldCount )
451 pushError( tr(
"Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( attributeCount ) );
453 attributes.resize( mFields.count() );
454 it->setAttributes( attributes );
464 pushError( tr(
"Could not add feature with geometry type %1 to layer of type %2" ).arg(
QgsWkbTypes::displayString( it->geometry().wkbType() ),
471 bool conversionError {
false };
472 QString errorMessage;
473 for (
int i = 0; i < mFields.count(); ++i )
475 const QVariant originalValue = it->attribute( i );
476 QVariant attrValue = originalValue;
477 if ( !
QgsVariantUtils::isNull( attrValue ) && ! mFields.at( i ).convertCompatible( attrValue, &errorMessage ) )
482 pushError( tr(
"Could not store attribute \"%1\": %2" )
483 .arg( mFields.at( i ).name(), errorMessage ) );
486 conversionError =
true;
489 else if ( attrValue.userType() != originalValue.userType() )
492 it->setAttribute( i, attrValue );
497 if ( conversionError )
506 mFeatures.insert( mNextFeatureId, *it );
507 addedFids.insert( mNextFeatureId );
509 if ( it->hasGeometry() )
512 mExtent.combineExtentWith( it->geometry().boundingBox() );
516 mSpatialIndex->addFeature( *it );
527 mFeatures.remove( addedFid );
530 mNextFeatureId = oldNextFeatureId;
540bool QgsMemoryProvider::deleteFeatures(
const QgsFeatureIds &
id )
542 for ( QgsFeatureIds::const_iterator it =
id.begin(); it !=
id.end(); ++it )
544 const QgsFeatureMap::iterator fit = mFeatures.find( *it );
547 if ( fit == mFeatures.end() )
552 mSpatialIndex->deleteFeature( *fit );
554 mFeatures.erase( fit );
563bool QgsMemoryProvider::addAttributes(
const QList<QgsField> &attributes )
565 bool fieldWasAdded {
false };
568 if ( !supportedType( field ) )
572 bool isNativeTypeName =
false;
573 NativeType nativeTypeCandidate( QString(), QString(), QMetaType::Type::UnknownType );
574 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
575 for (
const NativeType &nativeType : nativeTypesList )
577 if ( nativeType.mTypeName.toLower() == field.
typeName().toLower() )
579 isNativeTypeName =
true;
583 if ( nativeType.mType == field.
type()
584 && nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
585 nativeTypeCandidate = nativeType;
587 if ( !isNativeTypeName )
589 if ( nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
595 field.
setTypeName( nativeTypeCandidate.mTypeName );
599 mFields.append( field );
600 fieldWasAdded =
true;
602 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
606 attr.append( QVariant() );
610 return fieldWasAdded;
613bool QgsMemoryProvider::renameAttributes(
const QgsFieldNameMap &renamedAttributes )
615 QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
617 for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
619 const int fieldIndex = renameIt.key();
620 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
625 if ( mFields.indexFromName( renameIt.value() ) >= 0 )
632 mFields.rename( fieldIndex, renameIt.value() );
637bool QgsMemoryProvider::deleteAttributes(
const QgsAttributeIds &attributes )
639 QList<int> attrIdx( attributes.begin(), attributes.end() );
640 std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
643 for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
646 mFields.remove( idx );
648 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
662 bool result {
true };
666 QString errorMessage;
667 for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
669 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
670 if ( fit == mFeatures.end() )
677 for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
679 const int fieldIndex = it2.key();
680 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
683 QVariant attrValue = it2.value();
686 && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
687 if ( conversionError )
692 pushError( tr(
"Could not change attribute %1 having type %2 for feature %4: %3" )
693 .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
694 errorMessage ).arg( it.key() ) );
699 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
700 fit->setAttribute( it2.key(), attrValue );
702 rollBackMap.insert( it.key(), rollBackAttrs );
708 changeAttributeValues( rollBackMap );
717bool QgsMemoryProvider::changeGeometryValues(
const QgsGeometryMap &geometry_map )
719 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
721 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
722 if ( fit == mFeatures.end() )
727 mSpatialIndex->deleteFeature( *fit );
729 fit->setGeometry( it.value() );
733 mSpatialIndex->addFeature( *fit );
741QString QgsMemoryProvider::subsetString()
const
743 return mSubsetString;
746bool QgsMemoryProvider::setSubsetString(
const QString &theSQL,
bool updateFeatureCount )
748 Q_UNUSED( updateFeatureCount )
750 if ( !theSQL.isEmpty() )
753 if ( tempExpression.hasParserError() )
757 if ( theSQL == mSubsetString )
760 mSubsetString = theSQL;
768bool QgsMemoryProvider::supportsSubsetString()
const
773QString QgsMemoryProvider::subsetStringDialect()
const
775 return tr(
"QGIS expression" );
778QString QgsMemoryProvider::subsetStringHelpUrl()
const
784bool QgsMemoryProvider::createSpatialIndex()
786 if ( !mSpatialIndex )
791 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
793 mSpatialIndex->addFeature( *it );
811bool QgsMemoryProvider::truncate()
819void QgsMemoryProvider::updateExtents()
824QString QgsMemoryProvider::name()
const
826 return TEXT_PROVIDER_KEY;
829QString QgsMemoryProvider::description()
const
831 return TEXT_PROVIDER_DESCRIPTION;
835QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
836 :
QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
840QIcon QgsMemoryProviderMetadata::icon()
const
847 return new QgsMemoryProvider( uri, options, flags );
850QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes()
const
@ 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.
This class represents a coordinate reference system (CRS).
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)
Fetch next feature and stores in f, returns true on success.
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.
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.
void setNull()
Mark a rectangle as being null (holding no spatial information).
A spatial index for QgsFeature objects.
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.
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.