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++ )
 
  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() )
 
  364    mExtent.setMinimal();
 
  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 )
 
  498      if ( flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
 
  505    mFeatures.insert( mNextFeatureId, *it );
 
  506    addedFids.insert( mNextFeatureId );
 
  508    if ( it->hasGeometry() )
 
  511        mExtent.combineExtentWith( it->geometry().boundingBox() );
 
  515        mSpatialIndex->addFeature( *it );
 
  522  if ( ! result && flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
 
  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;
 
  583           && nativeTypeCandidate.mType == QVariant::Invalid )
 
  584        nativeTypeCandidate = nativeType;
 
  586    if ( !isNativeTypeName )
 
  588      if ( nativeTypeCandidate.mType == QVariant::Invalid )
 
  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      QVariant attrValue = it2.value();
 
  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 );
 
  712bool 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 );
 
  736QString QgsMemoryProvider::subsetString()
 const 
  738  return mSubsetString;
 
  741bool 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();
 
  763bool 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;
 
  783QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities()
 const 
  785  return AddFeatures | DeleteFeatures | ChangeGeometries |
 
  786         ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
 
  787         SelectAtId | CircularGeometries | FastTruncate;
 
  790bool QgsMemoryProvider::truncate()
 
  794  mExtent.setMinimal();
 
  798void QgsMemoryProvider::updateExtents()
 
  800  mExtent.setMinimal();
 
  803QString QgsMemoryProvider::name()
 const 
  805  return TEXT_PROVIDER_KEY;
 
  808QString QgsMemoryProvider::description()
 const 
  810  return TEXT_PROVIDER_DESCRIPTION;
 
  814QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
 
  815  : 
QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
 
  819QIcon QgsMemoryProviderMetadata::icon()
 const 
  826  return new QgsMemoryProvider( uri, options, flags );
 
  829QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes()
 const 
  831  return { Qgis::LayerType::Vector };
 
WkbType
The WKB type describes the number of dimensions a geometry has.
 
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).
 
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
 
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)
 
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.
 
static bool isNull(const QVariant &variant)
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) SIP_HOLDGIL
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) 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
 
Setting options for creating vector data providers.