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 );
 
  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 
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.
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.
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, bool silenceNullWarnings=false)
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.