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