17#include "moc_qgsmemoryprovider.cpp"
31#include <QRegularExpression>
36#define TEXT_PROVIDER_KEY QStringLiteral( "memory" )
37#define TEXT_PROVIDER_DESCRIPTION QStringLiteral( "Memory provider" )
44 const QUrl url = QUrl::fromEncoded( uri.toUtf8() );
45 const QUrlQuery query( url );
47 if ( query.hasQueryItem( QStringLiteral(
"geometry" ) ) )
49 geometry = query.queryItemValue( QStringLiteral(
"geometry" ) );
53 geometry = url.path();
56 if ( geometry.compare( QLatin1String(
"none" ), Qt::CaseInsensitive ) == 0 )
65 if ( query.hasQueryItem( QStringLiteral(
"crs" ) ) )
67 const QString crsDef = query.queryItemValue( QStringLiteral(
"crs" ) );
68 mCrs.createFromString( crsDef );
79 setNativeTypes( QList< NativeType >()
126 if ( query.hasQueryItem( QStringLiteral(
"field" ) ) )
128 QList<QgsField> attributes;
129 const thread_local QRegularExpression reFieldDef(
"\\:"
135 QRegularExpression::CaseInsensitiveOption );
136 const QStringList fields = query.allQueryItemValues( QStringLiteral(
"field" ) );
137 for (
int i = 0; i < fields.size(); i++ )
139 QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
140 const QRegularExpressionMatch regularExpressionMatch = reFieldDef.match( name );
143 QMetaType::Type type = QMetaType::Type::QString;
144 QMetaType::Type subType = QMetaType::Type::UnknownType;
145 QString
typeName( QStringLiteral(
"string" ) );
149 if ( regularExpressionMatch.hasMatch() )
151 name = name.mid( 0, regularExpressionMatch.capturedStart() );
152 typeName = regularExpressionMatch.captured( 1 ).toLower();
155 bool isNativeType =
false;
156 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
157 for (
const NativeType &nativeType : nativeTypesList )
159 if ( nativeType.mTypeName.toLower() ==
typeName )
162 type = nativeType.mType;
163 subType = nativeType.mSubType;
170 if ( isNativeType ==
false )
172 if (
typeName == QLatin1String(
"int" ) )
174 type = QMetaType::Type::Int;
175 typeName = QStringLiteral(
"integer" );
177 else if (
typeName == QLatin1String(
"long" ) )
179 type = QMetaType::Type::LongLong;
180 typeName = QStringLiteral(
"int8" );
182 else if (
typeName == QLatin1String(
"bool" ) )
184 type = QMetaType::Type::Bool;
185 typeName = QStringLiteral(
"boolean" );
190 type = QMetaType::Type::QString;
191 typeName = QStringLiteral(
"string" );
196 if (
typeName == QLatin1String(
"real" ) ||
typeName == QLatin1String(
"double" ) )
202 if ( !regularExpressionMatch.captured( 2 ).isEmpty() )
203 length = regularExpressionMatch.captured( 2 ).toInt();
205 if ( !regularExpressionMatch.captured( 3 ).isEmpty() )
206 precision = regularExpressionMatch.captured( 3 ).toInt();
209 if ( !regularExpressionMatch.captured( 4 ).isEmpty() )
211 if ( subType == QMetaType::Type::UnknownType )
214 if ( type != QMetaType::Type::QVariantList && type != QMetaType::Type::QStringList )
215 type = type == QMetaType::Type::QString ? QMetaType::Type::QStringList : QMetaType::Type::QVariantList;
217 const QLatin1String listSuffix(
"list" );
218 if ( !
typeName.endsWith( listSuffix ) )
219 typeName += QLatin1String(
"list" );
225 addAttributes( attributes );
228 if ( query.hasQueryItem( QStringLiteral(
"index" ) ) && query.queryItemValue( QStringLiteral(
"index" ) ) == QLatin1String(
"yes" ) )
230 createSpatialIndex();
235QgsMemoryProvider::~QgsMemoryProvider()
237 delete mSpatialIndex;
240QString QgsMemoryProvider::providerKey()
242 return TEXT_PROVIDER_KEY;
245QString QgsMemoryProvider::providerDescription()
247 return TEXT_PROVIDER_DESCRIPTION;
252 return new QgsMemoryFeatureSource(
this );
255QString QgsMemoryProvider::dataSourceUri(
bool expandAuthConfig )
const
257 Q_UNUSED( expandAuthConfig )
259 QUrl uri( QStringLiteral(
"memory" ) );
262 query.addQueryItem( QStringLiteral(
"geometry" ), geometry );
264 if ( mCrs.isValid() )
267 const QString authid = mCrs.authid();
268 if ( authid.startsWith( QLatin1String(
"EPSG:" ) ) )
276 query.addQueryItem( QStringLiteral(
"crs" ), crsDef );
280 query.addQueryItem( QStringLiteral(
"index" ), QStringLiteral(
"yes" ) );
284 for (
int i = 0; i < attrs.size(); i++ )
286 const QgsField field = mFields.at( attrs[i] );
287 QString fieldDef = field.
name();
291 if ( field.
type() == QMetaType::Type::QVariantList || field.
type() == QMetaType::Type::QStringList )
295 case QMetaType::Type::Int:
296 typeName = QStringLiteral(
"integer" );
299 case QMetaType::Type::LongLong:
300 typeName = QStringLiteral(
"long" );
303 case QMetaType::Type::Double:
304 typeName = QStringLiteral(
"double" );
307 case QMetaType::Type::QString:
308 typeName = QStringLiteral(
"string" );
317 fieldDef.append( QStringLiteral(
":%2(%3,%4)%5" ).arg(
typeName ).arg( field.
length() ).arg( field.
precision() ).arg( isList ? QStringLiteral(
"[]" ) : QString() ) );
318 query.addQueryItem( QStringLiteral(
"field" ), fieldDef );
320 uri.setQuery( query );
322 return QString( uri.toEncoded() );
326QString QgsMemoryProvider::storageType()
const
328 return QStringLiteral(
"Memory storage" );
333 return QgsFeatureIterator(
new QgsMemoryFeatureIterator(
new QgsMemoryFeatureSource(
this ),
true, request ) );
339 if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
342 if ( mSubsetString.isEmpty() )
345 const auto constMFeatures = mFeatures;
346 for (
const QgsFeature &feat : constMFeatures )
348 if ( feat.hasGeometry() )
349 mExtent.combineExtentWith( feat.geometry().boundingBox() );
363 else if ( mFeatures.isEmpty() )
376long long QgsMemoryProvider::featureCount()
const
378 if ( mSubsetString.isEmpty() )
379 return mFeatures.count();
392QgsFields QgsMemoryProvider::fields()
const
397bool QgsMemoryProvider::isValid()
const
410 if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
413 mFeatures = other->mFeatures;
414 mNextFeatureId = other->mNextFeatureId;
415 mExtent = other->mExtent;
420bool QgsMemoryProvider::addFeatures(
QgsFeatureList &flist, Flags flags )
424 const bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
426 const int fieldCount = mFields.count();
429 const auto oldExtent { mExtent };
430 const auto oldNextFeatureId { mNextFeatureId };
433 for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result ; ++it )
435 it->setId( mNextFeatureId );
436 it->setValid(
true );
437 const int attributeCount = it->attributeCount();
438 if ( attributeCount < fieldCount )
443 for (
int i = attributeCount; i < mFields.count(); ++i )
447 it->setAttributes( attributes );
449 else if ( attributeCount > fieldCount )
452 pushError( tr(
"Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( attributeCount ) );
454 attributes.resize( mFields.count() );
455 it->setAttributes( attributes );
465 pushError( tr(
"Could not add feature with geometry type %1 to layer of type %2" ).arg(
QgsWkbTypes::displayString( it->geometry().wkbType() ),
472 bool conversionError {
false };
473 QString errorMessage;
474 for (
int i = 0; i < mFields.count(); ++i )
476 const QVariant originalValue = it->attribute( i );
477 QVariant attrValue = originalValue;
478 if ( !
QgsVariantUtils::isNull( attrValue ) && ! mFields.at( i ).convertCompatible( attrValue, &errorMessage ) )
483 pushError( tr(
"Could not store attribute \"%1\": %2" )
484 .arg( mFields.at( i ).name(), errorMessage ) );
487 conversionError =
true;
490 else if ( attrValue.userType() != originalValue.userType() )
493 it->setAttribute( i, attrValue );
498 if ( conversionError )
507 mFeatures.insert( mNextFeatureId, *it );
508 addedFids.insert( mNextFeatureId );
510 if ( it->hasGeometry() )
513 mExtent.combineExtentWith( it->geometry().boundingBox() );
517 mSpatialIndex->addFeature( *it );
528 mFeatures.remove( addedFid );
531 mNextFeatureId = oldNextFeatureId;
541bool QgsMemoryProvider::deleteFeatures(
const QgsFeatureIds &
id )
543 for ( QgsFeatureIds::const_iterator it =
id.begin(); it !=
id.end(); ++it )
545 const QgsFeatureMap::iterator fit = mFeatures.find( *it );
548 if ( fit == mFeatures.end() )
553 mSpatialIndex->deleteFeature( *fit );
555 mFeatures.erase( fit );
564bool QgsMemoryProvider::addAttributes(
const QList<QgsField> &attributes )
566 bool fieldWasAdded {
false };
569 if ( !supportedType( field ) )
573 bool isNativeTypeName =
false;
574 NativeType nativeTypeCandidate( QString(), QString(), QMetaType::Type::UnknownType );
575 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
576 for (
const NativeType &nativeType : nativeTypesList )
578 if ( nativeType.mTypeName.toLower() == field.
typeName().toLower() )
580 isNativeTypeName =
true;
584 if ( nativeType.mType == field.
type()
585 && nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
586 nativeTypeCandidate = nativeType;
588 if ( !isNativeTypeName )
590 if ( nativeTypeCandidate.mType == QMetaType::Type::UnknownType )
596 field.
setTypeName( nativeTypeCandidate.mTypeName );
600 mFields.append( field );
601 fieldWasAdded =
true;
603 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
607 attr.append( QVariant() );
611 return fieldWasAdded;
614bool QgsMemoryProvider::renameAttributes(
const QgsFieldNameMap &renamedAttributes )
616 QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
618 for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
620 const int fieldIndex = renameIt.key();
621 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
626 if ( mFields.indexFromName( renameIt.value() ) >= 0 )
633 mFields.rename( fieldIndex, renameIt.value() );
638bool QgsMemoryProvider::deleteAttributes(
const QgsAttributeIds &attributes )
640 QList<int> attrIdx( attributes.begin(), attributes.end() );
641 std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
644 for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
647 mFields.remove( idx );
649 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
663 bool result {
true };
667 QString errorMessage;
668 for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
670 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
671 if ( fit == mFeatures.end() )
678 for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
680 const int fieldIndex = it2.key();
681 if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
684 QVariant attrValue = it2.value();
687 && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
688 if ( conversionError )
693 pushError( tr(
"Could not change attribute %1 having type %2 for feature %4: %3" )
694 .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
695 errorMessage ).arg( it.key() ) );
700 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
701 fit->setAttribute( it2.key(), attrValue );
703 rollBackMap.insert( it.key(), rollBackAttrs );
709 changeAttributeValues( rollBackMap );
718bool QgsMemoryProvider::changeGeometryValues(
const QgsGeometryMap &geometry_map )
720 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
722 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
723 if ( fit == mFeatures.end() )
728 mSpatialIndex->deleteFeature( *fit );
730 fit->setGeometry( it.value() );
734 mSpatialIndex->addFeature( *fit );
742QString QgsMemoryProvider::subsetString()
const
744 return mSubsetString;
747bool QgsMemoryProvider::setSubsetString(
const QString &theSQL,
bool updateFeatureCount )
749 Q_UNUSED( updateFeatureCount )
751 if ( !theSQL.isEmpty() )
754 if ( tempExpression.hasParserError() )
758 if ( theSQL == mSubsetString )
761 mSubsetString = theSQL;
769bool QgsMemoryProvider::supportsSubsetString()
const
774QString QgsMemoryProvider::subsetStringDialect()
const
776 return tr(
"QGIS expression" );
779QString QgsMemoryProvider::subsetStringHelpUrl()
const
785bool QgsMemoryProvider::createSpatialIndex()
787 if ( !mSpatialIndex )
792 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
794 mSpatialIndex->addFeature( *it );
812bool QgsMemoryProvider::truncate()
820void QgsMemoryProvider::updateExtents()
825QString QgsMemoryProvider::name()
const
827 return TEXT_PROVIDER_KEY;
830QString QgsMemoryProvider::description()
const
832 return TEXT_PROVIDER_DESCRIPTION;
836QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
837 :
QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
841QIcon QgsMemoryProviderMetadata::icon()
const
848 return new QgsMemoryProvider( uri, options, flags );
851QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes()
const
856#undef TEXT_PROVIDER_KEY
857#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.
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.