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()
 
  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();
 
  690                                   && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
 
  691      if ( conversionError )
 
  696          pushError( tr( 
"Could not change attribute %1 having type %2 for feature %4: %3" )
 
  697                     .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
 
  698                           errorMessage ).arg( it.key() ) );
 
  703      rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
 
  704      fit->setAttribute( it2.key(), attrValue );
 
  706    rollBackMap.insert( it.key(), rollBackAttrs );
 
  712    changeAttributeValues( rollBackMap );
 
  721bool QgsMemoryProvider::changeGeometryValues( 
const QgsGeometryMap &geometry_map )
 
  723  for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
 
  725    const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
 
  726    if ( fit == mFeatures.end() )
 
  731      mSpatialIndex->deleteFeature( *fit );
 
  733    fit->setGeometry( it.value() );
 
  737      mSpatialIndex->addFeature( *fit );
 
  745QString QgsMemoryProvider::subsetString()
 const 
  747  return mSubsetString;
 
  750bool QgsMemoryProvider::setSubsetString( 
const QString &theSQL, 
bool updateFeatureCount )
 
  752  Q_UNUSED( updateFeatureCount )
 
  754  if ( !theSQL.isEmpty() )
 
  757    if ( tempExpression.hasParserError() )
 
  761  if ( theSQL == mSubsetString )
 
  764  mSubsetString = theSQL;
 
  772bool QgsMemoryProvider::supportsSubsetString()
 const 
  777QString QgsMemoryProvider::subsetStringDialect()
 const 
  779  return tr( 
"QGIS expression" );
 
  782QString QgsMemoryProvider::subsetStringHelpUrl()
 const 
  788bool QgsMemoryProvider::createSpatialIndex()
 
  790  if ( !mSpatialIndex )
 
  792    mSpatialIndex = std::make_unique<QgsSpatialIndex>();
 
  795    for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
 
  797      mSpatialIndex->addFeature( *it );
 
  820bool QgsMemoryProvider::truncate()
 
  828void QgsMemoryProvider::updateExtents()
 
  833QString QgsMemoryProvider::name()
 const 
  835  return TEXT_PROVIDER_KEY;
 
  838QString QgsMemoryProvider::description()
 const 
  840  return TEXT_PROVIDER_DESCRIPTION;
 
  844QgsMemoryProviderMetadata::QgsMemoryProviderMetadata()
 
  845  : 
QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription() )
 
  849QIcon QgsMemoryProviderMetadata::icon()
 const 
  856  return new QgsMemoryProvider( uri, options, flags );
 
  859QList<Qgis::LayerType> QgsMemoryProviderMetadata::supportedLayerTypes()
 const 
  864#undef TEXT_PROVIDER_KEY 
  865#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.
 
Represents a coordinate reference system (CRS).
 
Abstract base class for spatial data provider implementations.
 
Handles 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.
 
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).
 
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.
 
static bool isUnsetAttributeValue(const QVariant &variant)
Check if the variant is a QgsUnsetAttributeValue.
 
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 Q_INVOKABLE 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.