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::createSpatialIndex()
770 if ( !mSpatialIndex )
775 for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
777 mSpatialIndex->addFeature( *it );
790 return AddFeatures | DeleteFeatures | ChangeGeometries |
791 ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
792 SelectAtId | CircularGeometries | FastTruncate;
795bool QgsMemoryProvider::truncate()
803void QgsMemoryProvider::updateExtents()
808QString QgsMemoryProvider::name()
const
810 return TEXT_PROVIDER_KEY;
813QString QgsMemoryProvider::description()
const
815 return TEXT_PROVIDER_DESCRIPTION;
819QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
820 :
QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
824QIcon QgsMemoryProviderMetadata::icon()
const
831 return new QgsMemoryProvider( uri, options, flags );
834QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes()
const
SpatialIndexPresence
Enumeration of spatial index presence states.
@ NotPresent
No spatial index exists for the source.
@ Present
A valid spatial index exists for the source.
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.
QFlags< ReadFlag > ReadFlags
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.
QFlags< Capability > Capabilities
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.