QGIS API Documentation  3.25.0-Master (6b426f5f8a)
qgsmemoryprovider.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  memoryprovider.cpp - provider with storage in memory
3  ------------------
4  begin : June 2008
5  copyright : (C) 2008 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsmemoryprovider.h"
18 
19 #include "qgsfeature.h"
20 #include "qgsfields.h"
21 #include "qgsgeometry.h"
22 #include "qgslogger.h"
23 #include "qgsspatialindex.h"
25 #include "qgsvariantutils.h"
26 
27 #include <QUrl>
28 #include <QUrlQuery>
29 #include <QRegularExpression>
30 
32 
33 #define TEXT_PROVIDER_KEY QStringLiteral( "memory" )
34 #define TEXT_PROVIDER_DESCRIPTION QStringLiteral( "Memory provider" )
35 
36 QgsMemoryProvider::QgsMemoryProvider( const QString &uri, const ProviderOptions &options, QgsDataProvider::ReadFlags flags )
37  : QgsVectorDataProvider( uri, options, flags )
38 {
39  // Initialize the geometry with the uri to support old style uri's
40  // (ie, just 'point', 'line', 'polygon')
41  const QUrl url = QUrl::fromEncoded( uri.toUtf8() );
42  const QUrlQuery query( url );
43  QString geometry;
44  if ( query.hasQueryItem( QStringLiteral( "geometry" ) ) )
45  {
46  geometry = query.queryItemValue( QStringLiteral( "geometry" ) );
47  }
48  else
49  {
50  geometry = url.path();
51  }
52 
53  if ( geometry.compare( QLatin1String( "none" ), Qt::CaseInsensitive ) == 0 )
54  {
55  mWkbType = QgsWkbTypes::NoGeometry;
56  }
57  else
58  {
59  mWkbType = QgsWkbTypes::parseType( geometry );
60  }
61 
62  if ( query.hasQueryItem( QStringLiteral( "crs" ) ) )
63  {
64  const QString crsDef = query.queryItemValue( QStringLiteral( "crs" ) );
65  mCrs.createFromString( crsDef );
66  }
67  else
68  {
69  // TODO - remove in QGIS 4.0. Layers without an explicit CRS set SHOULD have an invalid CRS. But in order to maintain
70  // 3.x api, we have to be tolerant/shortsighted(?) here and fallback to EPSG:4326
71  mCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );
72  }
73 
74  mNextFeatureId = 1;
75 
76  setNativeTypes( QList< NativeType >()
77  << QgsVectorDataProvider::NativeType( tr( "Whole Number (integer)" ), QStringLiteral( "integer" ), QVariant::Int, 0, 10 )
78  // Decimal number from OGR/Shapefile/dbf may come with length up to 32 and
79  // precision up to length-2 = 30 (default, if width is not specified in dbf is length = 24 precision = 15)
80  // We know that double (QVariant::Double) has only 15-16 significant numbers,
81  // but setting that correct limits would disable the use of memory provider with
82  // data from Shapefiles. In any case, the data are handled as doubles.
83  // So the limits set here are not correct but enable use of data from Shapefiles.
84  << QgsVectorDataProvider::NativeType( tr( "Decimal Number (real)" ), QStringLiteral( "double" ), QVariant::Double, 0, 32, 0, 30 )
85  << QgsVectorDataProvider::NativeType( tr( "Text (string)" ), QStringLiteral( "string" ), QVariant::String, 0, 255 )
86 
87  // date type
88  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::Date ), QStringLiteral( "date" ), QVariant::Date, -1, -1, -1, -1 )
89  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::Time ), QStringLiteral( "time" ), QVariant::Time, -1, -1, -1, -1 )
90  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::DateTime ), QStringLiteral( "datetime" ), QVariant::DateTime, -1, -1, -1, -1 )
91 
92  // integer types
93  << QgsVectorDataProvider::NativeType( tr( "Whole Number (smallint - 16bit)" ), QStringLiteral( "int2" ), QVariant::Int, -1, -1, 0, 0 )
94  << QgsVectorDataProvider::NativeType( tr( "Whole Number (integer - 32bit)" ), QStringLiteral( "int4" ), QVariant::Int, -1, -1, 0, 0 )
95  << QgsVectorDataProvider::NativeType( tr( "Whole Number (integer - 64bit)" ), QStringLiteral( "int8" ), QVariant::LongLong, -1, -1, 0, 0 )
96  << QgsVectorDataProvider::NativeType( tr( "Decimal Number (numeric)" ), QStringLiteral( "numeric" ), QVariant::Double, 1, 20, 0, 20 )
97  << QgsVectorDataProvider::NativeType( tr( "Decimal Number (decimal)" ), QStringLiteral( "decimal" ), QVariant::Double, 1, 20, 0, 20 )
98 
99  // floating point
100  << QgsVectorDataProvider::NativeType( tr( "Decimal Number (real)" ), QStringLiteral( "real" ), QVariant::Double, -1, -1, -1, -1 )
101  << QgsVectorDataProvider::NativeType( tr( "Decimal Number (double)" ), QStringLiteral( "double precision" ), QVariant::Double, -1, -1, -1, -1 )
102 
103  // string types
104  << QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (text)" ), QStringLiteral( "text" ), QVariant::String, -1, -1, -1, -1 )
105 
106  // boolean
107  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::Bool ), QStringLiteral( "boolean" ), QVariant::Bool )
108 
109  // blob
110  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::ByteArray ), QStringLiteral( "binary" ), QVariant::ByteArray )
111 
112  // list types
113  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::StringList ), QStringLiteral( "stringlist" ), QVariant::StringList, 0, 0, 0, 0, QVariant::String )
114  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::List, QVariant::Int ), QStringLiteral( "integerlist" ), QVariant::List, 0, 0, 0, 0, QVariant::Int )
115  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::List, QVariant::Double ), QStringLiteral( "doublelist" ), QVariant::List, 0, 0, 0, 0, QVariant::Double )
116  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::List, QVariant::LongLong ), QStringLiteral( "integer64list" ), QVariant::List, 0, 0, 0, 0, QVariant::LongLong )
117 
118  );
119 
120  if ( query.hasQueryItem( QStringLiteral( "field" ) ) )
121  {
122  QList<QgsField> attributes;
123  const thread_local QRegularExpression reFieldDef( "\\:"
124  "([\\w\\s]+)" // type
125  "(?:\\((\\-?\\d+)" // length
126  "(?:\\,(\\-?\\d+))?" // precision
127  "\\))?(\\[\\])?" // array
128  "$",
129  QRegularExpression::CaseInsensitiveOption );
130  const QStringList fields = query.allQueryItemValues( QStringLiteral( "field" ) );
131  for ( int i = 0; i < fields.size(); i++ )
132  {
133  QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
134  const QRegularExpressionMatch regularExpressionMatch = reFieldDef.match( name );
135 
136  // If no match -> use string as type
137  QVariant::Type type = QVariant::String;
138  QVariant::Type subType = QVariant::Invalid;
139  QString typeName( QStringLiteral( "string" ) );
140  int length = 255;
141  int precision = 0;
142 
143  if ( regularExpressionMatch.hasMatch() )
144  {
145  name = name.mid( 0, regularExpressionMatch.capturedStart() );
146  typeName = regularExpressionMatch.captured( 1 ).toLower();
147 
148  // Search typeName correspondence in native types
149  bool isNativeType = false;
150  const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
151  for ( const NativeType &nativeType : nativeTypesList )
152  {
153  if ( nativeType.mTypeName.toLower() == typeName )
154  {
155  isNativeType = true;
156  type = nativeType.mType;
157  subType = nativeType.mSubType;
158  typeName = nativeType.mTypeName;
159  break;
160  }
161  }
162 
163  // Not a native type -> check other supported types:
164  if ( isNativeType == false )
165  {
166  if ( typeName == QLatin1String( "int" ) )
167  {
168  type = QVariant::Int;
169  typeName = QStringLiteral( "integer" );
170  }
171  else if ( typeName == QLatin1String( "long" ) )
172  {
173  type = QVariant::LongLong;
174  typeName = QStringLiteral( "int8" );
175  }
176  else if ( typeName == QLatin1String( "bool" ) )
177  {
178  type = QVariant::Bool;
179  typeName = QStringLiteral( "boolean" );
180  }
181  else
182  {
183  QgsLogger::warning( tr( "Unsupported typeName '%1'. Will be handled as string." ).arg( typeName ) );
184  type = QVariant::String;
185  typeName = QStringLiteral( "string" );
186  }
187  }
188 
189  // Set default length/precision for double/real
190  if ( typeName == QLatin1String( "real" ) || typeName == QLatin1String( "double" ) )
191  {
192  length = 20;
193  precision = 5;
194  }
195 
196  if ( !regularExpressionMatch.captured( 2 ).isEmpty() )
197  length = regularExpressionMatch.captured( 2 ).toInt();
198 
199  if ( !regularExpressionMatch.captured( 3 ).isEmpty() )
200  precision = regularExpressionMatch.captured( 3 ).toInt();
201 
202  // Array
203  if ( !regularExpressionMatch.captured( 4 ).isEmpty() )
204  {
205  if ( subType == QVariant::Invalid )
206  subType = type;
207 
208  if ( type != QVariant::List && type != QVariant::StringList )
209  type = type == QVariant::String ? QVariant::StringList : QVariant::List;
210 
211  const QLatin1String listSuffix( "list" );
212  if ( !typeName.endsWith( listSuffix ) )
213  typeName += QLatin1String( "list" );
214  }
215  }
216 
217  attributes.append( QgsField( name, type, typeName, length, precision, QString(), subType ) );
218  }
219  addAttributes( attributes );
220  }
221 
222  if ( query.hasQueryItem( QStringLiteral( "index" ) ) && query.queryItemValue( QStringLiteral( "index" ) ) == QLatin1String( "yes" ) )
223  {
224  createSpatialIndex();
225  }
226 
227 }
228 
229 QgsMemoryProvider::~QgsMemoryProvider()
230 {
231  delete mSpatialIndex;
232 }
233 
234 QString QgsMemoryProvider::providerKey()
235 {
236  return TEXT_PROVIDER_KEY;
237 }
238 
239 QString QgsMemoryProvider::providerDescription()
240 {
241  return TEXT_PROVIDER_DESCRIPTION;
242 }
243 
244 QgsMemoryProvider *QgsMemoryProvider::createProvider( const QString &uri,
245  const ProviderOptions &options,
246  QgsDataProvider::ReadFlags flags )
247 {
248  return new QgsMemoryProvider( uri, options, flags );
249 }
250 
251 QgsAbstractFeatureSource *QgsMemoryProvider::featureSource() const
252 {
253  return new QgsMemoryFeatureSource( this );
254 }
255 
256 QString QgsMemoryProvider::dataSourceUri( bool expandAuthConfig ) const
257 {
258  Q_UNUSED( expandAuthConfig )
259 
260  QUrl uri( QStringLiteral( "memory" ) );
261  QUrlQuery query;
262  const QString geometry = QgsWkbTypes::displayString( mWkbType );
263  query.addQueryItem( QStringLiteral( "geometry" ), geometry );
264 
265  if ( mCrs.isValid() )
266  {
267  QString crsDef;
268  const QString authid = mCrs.authid();
269  if ( authid.startsWith( QLatin1String( "EPSG:" ) ) )
270  {
271  crsDef = authid;
272  }
273  else
274  {
275  crsDef = QStringLiteral( "wkt:%1" ).arg( mCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) );
276  }
277  query.addQueryItem( QStringLiteral( "crs" ), crsDef );
278  }
279  if ( mSpatialIndex )
280  {
281  query.addQueryItem( QStringLiteral( "index" ), QStringLiteral( "yes" ) );
282  }
283 
284  QgsAttributeList attrs = const_cast<QgsMemoryProvider *>( this )->attributeIndexes();
285  for ( int i = 0; i < attrs.size(); i++ )
286  {
287  const QgsField field = mFields.at( attrs[i] );
288  QString fieldDef = field.name();
289 
290  QString typeName = field.typeName();
291  bool isList = false;
292  if ( field.type() == QVariant::List || field.type() == QVariant::StringList )
293  {
294  switch ( field.subType() )
295  {
296  case QVariant::Int:
297  typeName = QStringLiteral( "integer" );
298  break;
299 
300  case QVariant::LongLong:
301  typeName = QStringLiteral( "long" );
302  break;
303 
304  case QVariant::Double:
305  typeName = QStringLiteral( "double" );
306  break;
307 
308  case QVariant::String:
309  typeName = QStringLiteral( "string" );
310  break;
311 
312  default:
313  break;
314  }
315  isList = true;
316  }
317 
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 );
320  }
321  uri.setQuery( query );
322 
323  return QString( uri.toEncoded() );
324 
325 }
326 
327 QString QgsMemoryProvider::storageType() const
328 {
329  return QStringLiteral( "Memory storage" );
330 }
331 
332 QgsFeatureIterator QgsMemoryProvider::getFeatures( const QgsFeatureRequest &request ) const
333 {
334  return QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, request ) );
335 }
336 
337 
338 QgsRectangle QgsMemoryProvider::extent() const
339 {
340  if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
341  {
342  mExtent.setMinimal();
343  if ( mSubsetString.isEmpty() )
344  {
345  // fast way - iterate through all features
346  const auto constMFeatures = mFeatures;
347  for ( const QgsFeature &feat : constMFeatures )
348  {
349  if ( feat.hasGeometry() )
350  mExtent.combineExtentWith( feat.geometry().boundingBox() );
351  }
352  }
353  else
354  {
355  QgsFeature f;
356  QgsFeatureIterator fi = getFeatures( QgsFeatureRequest().setNoAttributes() );
357  while ( fi.nextFeature( f ) )
358  {
359  if ( f.hasGeometry() )
360  mExtent.combineExtentWith( f.geometry().boundingBox() );
361  }
362  }
363  }
364  else if ( mFeatures.isEmpty() )
365  {
366  mExtent.setMinimal();
367  }
368 
369  return mExtent;
370 }
371 
372 QgsWkbTypes::Type QgsMemoryProvider::wkbType() const
373 {
374  return mWkbType;
375 }
376 
377 long long QgsMemoryProvider::featureCount() const
378 {
379  if ( mSubsetString.isEmpty() )
380  return mFeatures.count();
381 
382  // subset string set, no alternative but testing each feature
383  QgsFeatureIterator fit = QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, QgsFeatureRequest().setNoAttributes() ) );
384  long long count = 0;
385  QgsFeature feature;
386  while ( fit.nextFeature( feature ) )
387  {
388  count++;
389  }
390  return count;
391 }
392 
393 QgsFields QgsMemoryProvider::fields() const
394 {
395  return mFields;
396 }
397 
398 bool QgsMemoryProvider::isValid() const
399 {
400  return ( mWkbType != QgsWkbTypes::Unknown );
401 }
402 
404 {
405  // TODO: make provider projection-aware
406  return mCrs; // return default CRS
407 }
408 
409 void QgsMemoryProvider::handlePostCloneOperations( QgsVectorDataProvider *source )
410 {
411  if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
412  {
413  // these properties aren't copied when cloning a memory provider by uri, so we need to do it manually
414  mFeatures = other->mFeatures;
415  mNextFeatureId = other->mNextFeatureId;
416  mExtent = other->mExtent;
417  }
418 }
419 
420 // returns TRUE if all features were added successfully, or FALSE if any feature could not be added
421 bool QgsMemoryProvider::addFeatures( QgsFeatureList &flist, Flags flags )
422 {
423  bool result = true;
424  // whether or not to update the layer extent on the fly as we add features
425  const bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
426 
427  const int fieldCount = mFields.count();
428 
429  // For rollback
430  const auto oldExtent { mExtent };
431  const auto oldNextFeatureId { mNextFeatureId };
432  QgsFeatureIds addedFids ;
433 
434  for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result ; ++it )
435  {
436  it->setId( mNextFeatureId );
437  it->setValid( true );
438  if ( it->attributes().count() < fieldCount )
439  {
440  // ensure features have the correct number of attributes by padding
441  // them with null attributes for missing values
442  QgsAttributes attributes = it->attributes();
443  for ( int i = it->attributes().count(); i < mFields.count(); ++i )
444  {
445  attributes.append( QVariant( mFields.at( i ).type() ) );
446  }
447  it->setAttributes( attributes );
448  }
449  else if ( it->attributes().count() > fieldCount )
450  {
451  // too many attributes
452  pushError( tr( "Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( it->attributes().count() ) );
453  QgsAttributes attributes = it->attributes();
454  attributes.resize( mFields.count() );
455  it->setAttributes( attributes );
456  }
457 
458  if ( it->hasGeometry() && mWkbType == QgsWkbTypes::NoGeometry )
459  {
460  it->clearGeometry();
461  }
462  else if ( it->hasGeometry() && QgsWkbTypes::geometryType( it->geometry().wkbType() ) !=
463  QgsWkbTypes::geometryType( mWkbType ) )
464  {
465  pushError( tr( "Could not add feature with geometry type %1 to layer of type %2" ).arg( QgsWkbTypes::displayString( it->geometry().wkbType() ),
466  QgsWkbTypes::displayString( mWkbType ) ) );
467  result = false;
468  continue;
469  }
470 
471  // Check attribute conversion
472  bool conversionError { false };
473  QString errorMessage;
474  for ( int i = 0; i < mFields.count(); ++i )
475  {
476  const QVariant originalValue = it->attribute( i );
477  QVariant attrValue = originalValue;
478  if ( ! attrValue.isNull() && ! mFields.at( i ).convertCompatible( attrValue, &errorMessage ) )
479  {
480  // Push first conversion error only
481  if ( result )
482  {
483  pushError( tr( "Could not store attribute \"%1\": %2" )
484  .arg( mFields.at( i ).name(), errorMessage ) );
485  }
486  result = false;
487  conversionError = true;
488  continue;
489  }
490  else if ( attrValue.type() != originalValue.type() )
491  {
492  // convertCompatible has resulted in a data type conversion
493  it->setAttribute( i, attrValue );
494  }
495  }
496 
497  // Skip the feature if there is at least one conversion error
498  if ( conversionError )
499  {
500  if ( flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
501  {
502  break;
503  }
504  continue;
505  }
506 
507  mFeatures.insert( mNextFeatureId, *it );
508  addedFids.insert( mNextFeatureId );
509 
510  if ( it->hasGeometry() )
511  {
512  if ( updateExtent )
513  mExtent.combineExtentWith( it->geometry().boundingBox() );
514 
515  // update spatial index
516  if ( mSpatialIndex )
517  mSpatialIndex->addFeature( *it );
518  }
519 
520  mNextFeatureId++;
521  }
522 
523  // Roll back
524  if ( ! result && flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
525  {
526  for ( const QgsFeatureId &addedFid : addedFids )
527  {
528  mFeatures.remove( addedFid );
529  }
530  mExtent = oldExtent;
531  mNextFeatureId = oldNextFeatureId;
532  }
533  else
534  {
535  clearMinMaxCache();
536  }
537 
538  return result;
539 }
540 
541 bool QgsMemoryProvider::deleteFeatures( const QgsFeatureIds &id )
542 {
543  for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
544  {
545  const QgsFeatureMap::iterator fit = mFeatures.find( *it );
546 
547  // check whether such feature exists
548  if ( fit == mFeatures.end() )
549  continue;
550 
551  // update spatial index
552  if ( mSpatialIndex )
553  mSpatialIndex->deleteFeature( *fit );
554 
555  mFeatures.erase( fit );
556  }
557 
558  updateExtents();
559  clearMinMaxCache();
560 
561  return true;
562 }
563 
564 bool QgsMemoryProvider::addAttributes( const QList<QgsField> &attributes )
565 {
566  for ( QgsField field : attributes )
567  {
568  if ( !supportedType( field ) )
569  continue;
570 
571  // Make sure added attributes typeName correspond to a native type name
572  bool isNativeTypeName = false;
573  NativeType nativeTypeCandidate( QString(), QString(), QVariant::Invalid );
574  const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
575  for ( const NativeType &nativeType : nativeTypesList )
576  {
577  if ( nativeType.mTypeName.toLower() == field.typeName().toLower() )
578  {
579  isNativeTypeName = true;
580  break;
581  }
582 
583  if ( nativeType.mType == field.type()
584  && nativeTypeCandidate.mType == QVariant::Invalid )
585  nativeTypeCandidate = nativeType;
586  }
587  if ( !isNativeTypeName )
588  {
589  if ( nativeTypeCandidate.mType == QVariant::Invalid )
590  {
591  QgsLogger::warning( "Field type not supported: " + field.typeName() );
592  continue;
593  }
594 
595  field.setTypeName( nativeTypeCandidate.mTypeName );
596  }
597 
598  // add new field as a last one
599  mFields.append( field );
600 
601  for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
602  {
603  QgsFeature &f = fit.value();
604  QgsAttributes attr = f.attributes();
605  attr.append( QVariant() );
606  f.setAttributes( attr );
607  }
608  }
609  return true;
610 }
611 
612 bool QgsMemoryProvider::renameAttributes( const QgsFieldNameMap &renamedAttributes )
613 {
614  QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
615  bool result = true;
616  for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
617  {
618  const int fieldIndex = renameIt.key();
619  if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
620  {
621  result = false;
622  continue;
623  }
624  if ( mFields.indexFromName( renameIt.value() ) >= 0 )
625  {
626  //field name already in use
627  result = false;
628  continue;
629  }
630 
631  mFields.rename( fieldIndex, renameIt.value() );
632  }
633  return result;
634 }
635 
636 bool QgsMemoryProvider::deleteAttributes( const QgsAttributeIds &attributes )
637 {
638  QList<int> attrIdx = qgis::setToList( attributes );
639  std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
640 
641  // delete attributes one-by-one with decreasing index
642  for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
643  {
644  const int idx = *it;
645  mFields.remove( idx );
646 
647  for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
648  {
649  QgsFeature &f = fit.value();
650  QgsAttributes attr = f.attributes();
651  attr.remove( idx );
652  f.setAttributes( attr );
653  }
654  }
655  clearMinMaxCache();
656  return true;
657 }
658 
659 bool QgsMemoryProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
660 {
661  bool result { true };
662 
663  QgsChangedAttributesMap rollBackMap;
664 
665  QString errorMessage;
666  for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
667  {
668  const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
669  if ( fit == mFeatures.end() )
670  continue;
671 
672  const QgsAttributeMap &attrs = it.value();
673  QgsAttributeMap rollBackAttrs;
674 
675  // Break on errors
676  for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
677  {
678  QVariant attrValue = it2.value();
679  // Check attribute conversion
680  const bool conversionError { ! attrValue.isNull()
681  && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
682  if ( conversionError )
683  {
684  // Push first conversion error only
685  if ( result )
686  {
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() ) );
690  }
691  result = false;
692  break;
693  }
694  rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
695  fit->setAttribute( it2.key(), attrValue );
696  }
697  rollBackMap.insert( it.key(), rollBackAttrs );
698  }
699 
700  // Roll back
701  if ( ! result )
702  {
703  changeAttributeValues( rollBackMap );
704  }
705  else
706  {
707  clearMinMaxCache();
708  }
709  return result;
710 }
711 
712 bool QgsMemoryProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
713 {
714  for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
715  {
716  const QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
717  if ( fit == mFeatures.end() )
718  continue;
719 
720  // update spatial index
721  if ( mSpatialIndex )
722  mSpatialIndex->deleteFeature( *fit );
723 
724  fit->setGeometry( it.value() );
725 
726  // update spatial index
727  if ( mSpatialIndex )
728  mSpatialIndex->addFeature( *fit );
729  }
730 
731  updateExtents();
732 
733  return true;
734 }
735 
736 QString QgsMemoryProvider::subsetString() const
737 {
738  return mSubsetString;
739 }
740 
741 bool QgsMemoryProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount )
742 {
743  Q_UNUSED( updateFeatureCount )
744 
745  if ( !theSQL.isEmpty() )
746  {
747  const QgsExpression tempExpression( theSQL );
748  if ( tempExpression.hasParserError() )
749  return false;
750  }
751 
752  if ( theSQL == mSubsetString )
753  return true;
754 
755  mSubsetString = theSQL;
756  clearMinMaxCache();
757  mExtent.setMinimal();
758 
759  emit dataChanged();
760  return true;
761 }
762 
763 bool QgsMemoryProvider::createSpatialIndex()
764 {
765  if ( !mSpatialIndex )
766  {
767  mSpatialIndex = new QgsSpatialIndex();
768 
769  // add existing features to index
770  for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
771  {
772  mSpatialIndex->addFeature( *it );
773  }
774  }
775  return true;
776 }
777 
778 QgsFeatureSource::SpatialIndexPresence QgsMemoryProvider::hasSpatialIndex() const
779 {
780  return mSpatialIndex ? SpatialIndexPresent : SpatialIndexNotPresent;
781 }
782 
783 QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities() const
784 {
785  return AddFeatures | DeleteFeatures | ChangeGeometries |
786  ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
787  SelectAtId | CircularGeometries | FastTruncate;
788 }
789 
790 bool QgsMemoryProvider::truncate()
791 {
792  mFeatures.clear();
793  clearMinMaxCache();
794  mExtent.setMinimal();
795  return true;
796 }
797 
798 void QgsMemoryProvider::updateExtents()
799 {
800  mExtent.setMinimal();
801 }
802 
803 QString QgsMemoryProvider::name() const
804 {
805  return TEXT_PROVIDER_KEY;
806 }
807 
808 QString QgsMemoryProvider::description() const
809 {
810  return TEXT_PROVIDER_DESCRIPTION;
811 }
812 
Base class that can be used for any class that is capable of returning features.
A vector of attributes.
Definition: qgsattributes.h:58
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...
Definition: qgsfeature.h:56
QgsAttributes attributes
Definition: qgsfeature.h:65
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:153
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:223
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString typeName() const
Gets the field type.
Definition: qgsfield.cpp:139
QString name
Definition: qgsfield.h:60
int precision
Definition: qgsfield.h:57
int length
Definition: qgsfield.h:56
QVariant::Type type
Definition: qgsfield.h:58
QVariant::Type subType() const
If the field is a collection, gets its element's type.
Definition: qgsfield.cpp:134
void setTypeName(const QString &typeName)
Set the field type.
Definition: qgsfield.cpp:190
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void setMinimal() SIP_HOLDGIL
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:172
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...
Definition: qgswkbtypes.h:968
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.
Definition: qgswkbtypes.h:70
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
Definition: qgsattributes.h:44
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
Definition: qgsfeature.h:877
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
Definition: qgsfeature.h:868
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:882
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
QList< int > QgsAttributeList
Definition: qgsfield.h:26
const QgsField & field
Definition: qgsfield.h:463
QSet< int > QgsAttributeIds
const QgsCoordinateReferenceSystem & crs
const QString & typeName
int precision
const QgsAttributeList & attributeIndexes