29 #include <QRegularExpression>
33 #define TEXT_PROVIDER_KEY QStringLiteral( "memory" )
34 #define TEXT_PROVIDER_DESCRIPTION QStringLiteral( "Memory provider" )
36 QgsMemoryProvider::QgsMemoryProvider(
const QString &uri,
const ProviderOptions &options, QgsDataProvider::ReadFlags flags )
41 const QUrl url = QUrl::fromEncoded( uri.toUtf8() );
42 const QUrlQuery query( url );
44 if ( query.hasQueryItem( QStringLiteral(
"geometry" ) ) )
46 geometry = query.queryItemValue( QStringLiteral(
"geometry" ) );
50 geometry = url.path();
53 if ( geometry.compare( QLatin1String(
"none" ), Qt::CaseInsensitive ) == 0 )
62 if ( query.hasQueryItem( QStringLiteral(
"crs" ) ) )
64 const QString crsDef = query.queryItemValue( QStringLiteral(
"crs" ) );
65 mCrs.createFromString( crsDef );
76 setNativeTypes( QList< NativeType >()
120 if ( query.hasQueryItem( QStringLiteral(
"field" ) ) )
122 QList<QgsField> attributes;
123 const thread_local QRegularExpression reFieldDef(
"\\:"
129 QRegularExpression::CaseInsensitiveOption );
130 const QStringList fields = query.allQueryItemValues( QStringLiteral(
"field" ) );
131 for (
int i = 0; i < fields.size(); i++ )
133 QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
134 const QRegularExpressionMatch regularExpressionMatch = reFieldDef.match( name );
137 QVariant::Type type = QVariant::String;
138 QVariant::Type subType = QVariant::Invalid;
139 QString
typeName( QStringLiteral(
"string" ) );
143 if ( regularExpressionMatch.hasMatch() )
145 name = name.mid( 0, regularExpressionMatch.capturedStart() );
146 typeName = regularExpressionMatch.captured( 1 ).toLower();
149 bool isNativeType =
false;
150 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
151 for (
const NativeType &nativeType : nativeTypesList )
153 if ( nativeType.mTypeName.toLower() ==
typeName )
156 type = nativeType.mType;
157 subType = nativeType.mSubType;
164 if ( isNativeType ==
false )
166 if (
typeName == QLatin1String(
"int" ) )
168 type = QVariant::Int;
169 typeName = QStringLiteral(
"integer" );
171 else if (
typeName == QLatin1String(
"long" ) )
173 type = QVariant::LongLong;
174 typeName = QStringLiteral(
"int8" );
176 else if (
typeName == QLatin1String(
"bool" ) )
178 type = QVariant::Bool;
179 typeName = QStringLiteral(
"boolean" );
184 type = QVariant::String;
185 typeName = QStringLiteral(
"string" );
190 if (
typeName == QLatin1String(
"real" ) ||
typeName == QLatin1String(
"double" ) )
196 if ( !regularExpressionMatch.captured( 2 ).isEmpty() )
197 length = regularExpressionMatch.captured( 2 ).toInt();
199 if ( !regularExpressionMatch.captured( 3 ).isEmpty() )
200 precision = regularExpressionMatch.captured( 3 ).toInt();
203 if ( !regularExpressionMatch.captured( 4 ).isEmpty() )
205 if ( subType == QVariant::Invalid )
208 if ( type != QVariant::List && type != QVariant::StringList )
209 type = type == QVariant::String ? QVariant::StringList : QVariant::List;
211 const QLatin1String listSuffix(
"list" );
212 if ( !
typeName.endsWith( listSuffix ) )
213 typeName += QLatin1String(
"list" );
219 addAttributes( attributes );
222 if ( query.hasQueryItem( QStringLiteral(
"index" ) ) && query.queryItemValue( QStringLiteral(
"index" ) ) == QLatin1String(
"yes" ) )
224 createSpatialIndex();
229 QgsMemoryProvider::~QgsMemoryProvider()
231 delete mSpatialIndex;
234 QString QgsMemoryProvider::providerKey()
236 return TEXT_PROVIDER_KEY;
239 QString QgsMemoryProvider::providerDescription()
241 return TEXT_PROVIDER_DESCRIPTION;
244 QgsMemoryProvider *QgsMemoryProvider::createProvider(
const QString &uri,
245 const ProviderOptions &options,
246 QgsDataProvider::ReadFlags flags )
248 return new QgsMemoryProvider( uri, options, flags );
253 return new QgsMemoryFeatureSource(
this );
256 QString 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" ) );
285 for (
int i = 0; i < attrs.size(); i++ )
297 typeName = QStringLiteral(
"integer" );
300 case QVariant::LongLong:
301 typeName = QStringLiteral(
"long" );
304 case QVariant::Double:
305 typeName = QStringLiteral(
"double" );
308 case QVariant::String:
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() );
327 QString 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() )
366 mExtent.setMinimal();
377 long long QgsMemoryProvider::featureCount()
const
379 if ( mSubsetString.isEmpty() )
380 return mFeatures.count();
393 QgsFields QgsMemoryProvider::fields()
const
398 bool QgsMemoryProvider::isValid()
const
411 if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
414 mFeatures = other->mFeatures;
415 mNextFeatureId = other->mNextFeatureId;
416 mExtent = other->mExtent;
421 bool 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 if ( it->attributes().count() < fieldCount )
443 for (
int i = it->attributes().count(); i < mFields.count(); ++i )
445 attributes.append( QVariant( mFields.at( i ).type() ) );
447 it->setAttributes( attributes );
449 else if ( it->attributes().count() > fieldCount )
452 pushError( tr(
"Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( it->attributes().count() ) );
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 ( ! attrValue.isNull() && ! 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.type() != originalValue.type() )
493 it->setAttribute( i, attrValue );
498 if ( conversionError )
500 if ( flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
507 mFeatures.insert( mNextFeatureId, *it );
508 addedFids.insert( mNextFeatureId );
510 if ( it->hasGeometry() )
513 mExtent.combineExtentWith( it->geometry().boundingBox() );
517 mSpatialIndex->addFeature( *it );
524 if ( ! result && flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
528 mFeatures.remove( addedFid );
531 mNextFeatureId = oldNextFeatureId;
541 bool 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 );
564 bool QgsMemoryProvider::addAttributes(
const QList<QgsField> &attributes )
568 if ( !supportedType(
field ) )
572 bool isNativeTypeName =
false;
573 NativeType nativeTypeCandidate( QString(), QString(), QVariant::Invalid );
574 const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
575 for (
const NativeType &nativeType : nativeTypesList )
577 if ( nativeType.mTypeName.toLower() ==
field.
typeName().toLower() )
579 isNativeTypeName =
true;
584 && nativeTypeCandidate.mType == QVariant::Invalid )
585 nativeTypeCandidate = nativeType;
587 if ( !isNativeTypeName )
589 if ( nativeTypeCandidate.mType == QVariant::Invalid )
599 mFields.append(
field );
601 for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
605 attr.append( QVariant() );
612 bool 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() );
636 bool QgsMemoryProvider::deleteAttributes(
const QgsAttributeIds &attributes )
638 QList<int> attrIdx = qgis::setToList( attributes );
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 QVariant attrValue = it2.value();
680 const bool conversionError { ! attrValue.isNull()
681 && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
682 if ( conversionError )
687 pushError( tr(
"Could not change attribute %1 having type %2 for feature %4: %3" )
688 .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
689 errorMessage ).arg( it.key() ) );
694 rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
695 fit->setAttribute( it2.key(), attrValue );
697 rollBackMap.insert( it.key(), rollBackAttrs );
703 changeAttributeValues( rollBackMap );
712 bool QgsMemoryProvider::changeGeometryValues(
const QgsGeometryMap &geometry_map )
714 for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
716 const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
717 if ( fit == mFeatures.end() )
722 mSpatialIndex->deleteFeature( *fit );
724 fit->setGeometry( it.value() );
728 mSpatialIndex->addFeature( *fit );
736 QString QgsMemoryProvider::subsetString()
const
738 return mSubsetString;
741 bool QgsMemoryProvider::setSubsetString(
const QString &theSQL,
bool updateFeatureCount )
743 Q_UNUSED( updateFeatureCount )
745 if ( !theSQL.isEmpty() )
748 if ( tempExpression.hasParserError() )
752 if ( theSQL == mSubsetString )
755 mSubsetString = theSQL;
757 mExtent.setMinimal();
763 bool QgsMemoryProvider::createSpatialIndex()
765 if ( !mSpatialIndex )
770 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
772 mSpatialIndex->addFeature( *it );
780 return mSpatialIndex ? SpatialIndexPresent : SpatialIndexNotPresent;
783 QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities()
const
785 return AddFeatures | DeleteFeatures | ChangeGeometries |
786 ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
787 SelectAtId | CircularGeometries | FastTruncate;
790 bool QgsMemoryProvider::truncate()
794 mExtent.setMinimal();
798 void QgsMemoryProvider::updateExtents()
800 mExtent.setMinimal();
803 QString QgsMemoryProvider::name()
const
805 return TEXT_PROVIDER_KEY;
808 QString QgsMemoryProvider::description()
const
810 return TEXT_PROVIDER_DESCRIPTION;
Base class that can be used for any class that is capable of returning features.
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,...
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.
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...
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