QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 
26 #include <QUrl>
27 #include <QUrlQuery>
28 #include <QRegularExpression>
29 
31 
32 #define TEXT_PROVIDER_KEY QStringLiteral( "memory" )
33 #define TEXT_PROVIDER_DESCRIPTION QStringLiteral( "Memory provider" )
34 
35 QgsMemoryProvider::QgsMemoryProvider( const QString &uri, const ProviderOptions &options, QgsDataProvider::ReadFlags flags )
36  : QgsVectorDataProvider( uri, options, flags )
37 {
38  // Initialize the geometry with the uri to support old style uri's
39  // (ie, just 'point', 'line', 'polygon')
40  const QUrl url = QUrl::fromEncoded( uri.toUtf8() );
41  const QUrlQuery query( url );
42  QString geometry;
43  if ( query.hasQueryItem( QStringLiteral( "geometry" ) ) )
44  {
45  geometry = query.queryItemValue( QStringLiteral( "geometry" ) );
46  }
47  else
48  {
49  geometry = url.path();
50  }
51 
52  if ( geometry.compare( QLatin1String( "none" ), Qt::CaseInsensitive ) == 0 )
53  {
54  mWkbType = QgsWkbTypes::NoGeometry;
55  }
56  else
57  {
58  mWkbType = QgsWkbTypes::parseType( geometry );
59  }
60 
61  if ( query.hasQueryItem( QStringLiteral( "crs" ) ) )
62  {
63  const QString crsDef = query.queryItemValue( QStringLiteral( "crs" ) );
64  mCrs.createFromString( crsDef );
65  }
66  else
67  {
68  // TODO - remove in QGIS 4.0. Layers without an explicit CRS set SHOULD have an invalid CRS. But in order to maintain
69  // 3.x api, we have to be tolerant/shortsighted(?) here and fallback to EPSG:4326
70  mCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );
71  }
72 
73  mNextFeatureId = 1;
74 
75  setNativeTypes( QList< NativeType >()
76  << QgsVectorDataProvider::NativeType( tr( "Whole number (integer)" ), QStringLiteral( "integer" ), QVariant::Int, 0, 10 )
77  // Decimal number from OGR/Shapefile/dbf may come with length up to 32 and
78  // precision up to length-2 = 30 (default, if width is not specified in dbf is length = 24 precision = 15)
79  // We know that double (QVariant::Double) has only 15-16 significant numbers,
80  // but setting that correct limits would disable the use of memory provider with
81  // data from Shapefiles. In any case, the data are handled as doubles.
82  // So the limits set here are not correct but enable use of data from Shapefiles.
83  << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), QStringLiteral( "double" ), QVariant::Double, 0, 32, 0, 30 )
84  << QgsVectorDataProvider::NativeType( tr( "Text (string)" ), QStringLiteral( "string" ), QVariant::String, 0, 255 )
85 
86  // date type
87  << QgsVectorDataProvider::NativeType( tr( "Date" ), QStringLiteral( "date" ), QVariant::Date, -1, -1, -1, -1 )
88  << QgsVectorDataProvider::NativeType( tr( "Time" ), QStringLiteral( "time" ), QVariant::Time, -1, -1, -1, -1 )
89  << QgsVectorDataProvider::NativeType( tr( "Date & Time" ), QStringLiteral( "datetime" ), QVariant::DateTime, -1, -1, -1, -1 )
90 
91  // integer types
92  << QgsVectorDataProvider::NativeType( tr( "Whole number (smallint - 16bit)" ), QStringLiteral( "int2" ), QVariant::Int, -1, -1, 0, 0 )
93  << QgsVectorDataProvider::NativeType( tr( "Whole number (integer - 32bit)" ), QStringLiteral( "int4" ), QVariant::Int, -1, -1, 0, 0 )
94  << QgsVectorDataProvider::NativeType( tr( "Whole number (integer - 64bit)" ), QStringLiteral( "int8" ), QVariant::LongLong, -1, -1, 0, 0 )
95  << QgsVectorDataProvider::NativeType( tr( "Decimal number (numeric)" ), QStringLiteral( "numeric" ), QVariant::Double, 1, 20, 0, 20 )
96  << QgsVectorDataProvider::NativeType( tr( "Decimal number (decimal)" ), QStringLiteral( "decimal" ), QVariant::Double, 1, 20, 0, 20 )
97 
98  // floating point
99  << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), QStringLiteral( "real" ), QVariant::Double, -1, -1, -1, -1 )
100  << QgsVectorDataProvider::NativeType( tr( "Decimal number (double)" ), QStringLiteral( "double precision" ), QVariant::Double, -1, -1, -1, -1 )
101 
102  // string types
103  << QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (text)" ), QStringLiteral( "text" ), QVariant::String, -1, -1, -1, -1 )
104 
105  // boolean
106  << QgsVectorDataProvider::NativeType( tr( "Boolean" ), QStringLiteral( "boolean" ), QVariant::Bool )
107 
108  // blob
109  << QgsVectorDataProvider::NativeType( tr( "Binary object (BLOB)" ), QStringLiteral( "binary" ), QVariant::ByteArray )
110 
111  // list types
112  << QgsVectorDataProvider::NativeType( tr( "String list" ), QStringLiteral( "stringlist" ), QVariant::StringList, 0, 0, 0, 0, QVariant::String )
113  << QgsVectorDataProvider::NativeType( tr( "Integer list" ), QStringLiteral( "integerlist" ), QVariant::List, 0, 0, 0, 0, QVariant::Int )
114  << QgsVectorDataProvider::NativeType( tr( "Decimal (real) list" ), QStringLiteral( "doublelist" ), QVariant::List, 0, 0, 0, 0, QVariant::Double )
115  << QgsVectorDataProvider::NativeType( tr( "Integer (64bit) list" ), QStringLiteral( "integer64list" ), QVariant::List, 0, 0, 0, 0, QVariant::LongLong )
116 
117  );
118 
119  if ( query.hasQueryItem( QStringLiteral( "field" ) ) )
120  {
121  QList<QgsField> attributes;
122  const thread_local QRegularExpression reFieldDef( "\\:"
123  "([\\w\\s]+)" // type
124  "(?:\\((\\-?\\d+)" // length
125  "(?:\\,(\\-?\\d+))?" // precision
126  "\\))?(\\[\\])?" // array
127  "$",
128  QRegularExpression::CaseInsensitiveOption );
129  const QStringList fields = query.allQueryItemValues( QStringLiteral( "field" ) );
130  for ( int i = 0; i < fields.size(); i++ )
131  {
132  QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
133  const QRegularExpressionMatch regularExpressionMatch = reFieldDef.match( name );
134 
135  // If no match -> use string as type
136  QVariant::Type type = QVariant::String;
137  QVariant::Type subType = QVariant::Invalid;
138  QString typeName( QStringLiteral( "string" ) );
139  int length = 255;
140  int precision = 0;
141 
142  if ( regularExpressionMatch.hasMatch() )
143  {
144  name = name.mid( 0, regularExpressionMatch.capturedStart() );
145  typeName = regularExpressionMatch.captured( 1 ).toLower();
146 
147  // Search typeName correspondence in native types
148  bool isNativeType = false;
149  const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
150  for ( const NativeType &nativeType : nativeTypesList )
151  {
152  if ( nativeType.mTypeName.toLower() == typeName )
153  {
154  isNativeType = true;
155  type = nativeType.mType;
156  subType = nativeType.mSubType;
157  typeName = nativeType.mTypeName;
158  break;
159  }
160  }
161 
162  // Not a native type -> check other supported types:
163  if ( isNativeType == false )
164  {
165  if ( typeName == QLatin1String( "int" ) )
166  {
167  type = QVariant::Int;
168  typeName = QStringLiteral( "integer" );
169  }
170  else if ( typeName == QLatin1String( "long" ) )
171  {
172  type = QVariant::LongLong;
173  typeName = QStringLiteral( "int8" );
174  }
175  else if ( typeName == QLatin1String( "bool" ) )
176  {
177  type = QVariant::Bool;
178  typeName = QStringLiteral( "boolean" );
179  }
180  else
181  {
182  QgsLogger::warning( tr( "Unsupported typeName '%1'. Will be handled as string." ).arg( typeName ) );
183  type = QVariant::String;
184  typeName = QStringLiteral( "string" );
185  }
186  }
187 
188  // Set default length/precision for double/real
189  if ( typeName == QLatin1String( "real" ) || typeName == QLatin1String( "double" ) )
190  {
191  length = 20;
192  precision = 5;
193  }
194 
195  if ( !regularExpressionMatch.captured( 2 ).isEmpty() )
196  length = regularExpressionMatch.captured( 2 ).toInt();
197 
198  if ( !regularExpressionMatch.captured( 3 ).isEmpty() )
199  precision = regularExpressionMatch.captured( 3 ).toInt();
200 
201  // Array
202  if ( !regularExpressionMatch.captured( 4 ).isEmpty() )
203  {
204  if ( subType == QVariant::Invalid )
205  subType = type;
206 
207  if ( type != QVariant::List && type != QVariant::StringList )
208  type = type == QVariant::String ? QVariant::StringList : QVariant::List;
209 
210  const QLatin1String listSuffix( "list" );
211  if ( !typeName.endsWith( listSuffix ) )
212  typeName += QLatin1String( "list" );
213  }
214  }
215 
216  attributes.append( QgsField( name, type, typeName, length, precision, QString(), subType ) );
217  }
218  addAttributes( attributes );
219  }
220 
221  if ( query.hasQueryItem( QStringLiteral( "index" ) ) && query.queryItemValue( QStringLiteral( "index" ) ) == QLatin1String( "yes" ) )
222  {
223  createSpatialIndex();
224  }
225 
226 }
227 
228 QgsMemoryProvider::~QgsMemoryProvider()
229 {
230  delete mSpatialIndex;
231 }
232 
233 QString QgsMemoryProvider::providerKey()
234 {
235  return TEXT_PROVIDER_KEY;
236 }
237 
238 QString QgsMemoryProvider::providerDescription()
239 {
240  return TEXT_PROVIDER_DESCRIPTION;
241 }
242 
243 QgsMemoryProvider *QgsMemoryProvider::createProvider( const QString &uri,
244  const ProviderOptions &options,
245  QgsDataProvider::ReadFlags flags )
246 {
247  return new QgsMemoryProvider( uri, options, flags );
248 }
249 
250 QgsAbstractFeatureSource *QgsMemoryProvider::featureSource() const
251 {
252  return new QgsMemoryFeatureSource( this );
253 }
254 
255 QString QgsMemoryProvider::dataSourceUri( bool expandAuthConfig ) const
256 {
257  Q_UNUSED( expandAuthConfig )
258 
259  QUrl uri( QStringLiteral( "memory" ) );
260  QUrlQuery query;
261  const QString geometry = QgsWkbTypes::displayString( mWkbType );
262  query.addQueryItem( QStringLiteral( "geometry" ), geometry );
263 
264  if ( mCrs.isValid() )
265  {
266  QString crsDef;
267  const QString authid = mCrs.authid();
268  if ( authid.startsWith( QLatin1String( "EPSG:" ) ) )
269  {
270  crsDef = authid;
271  }
272  else
273  {
274  crsDef = QStringLiteral( "wkt:%1" ).arg( mCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) );
275  }
276  query.addQueryItem( QStringLiteral( "crs" ), crsDef );
277  }
278  if ( mSpatialIndex )
279  {
280  query.addQueryItem( QStringLiteral( "index" ), QStringLiteral( "yes" ) );
281  }
282 
283  QgsAttributeList attrs = const_cast<QgsMemoryProvider *>( this )->attributeIndexes();
284  for ( int i = 0; i < attrs.size(); i++ )
285  {
286  const QgsField field = mFields.at( attrs[i] );
287  QString fieldDef = field.name();
288 
289  QString typeName = field.typeName();
290  bool isList = false;
291  if ( field.type() == QVariant::List || field.type() == QVariant::StringList )
292  {
293  switch ( field.subType() )
294  {
295  case QVariant::Int:
296  typeName = QStringLiteral( "integer" );
297  break;
298 
299  case QVariant::LongLong:
300  typeName = QStringLiteral( "long" );
301  break;
302 
303  case QVariant::Double:
304  typeName = QStringLiteral( "double" );
305  break;
306 
307  case QVariant::String:
308  typeName = QStringLiteral( "string" );
309  break;
310 
311  default:
312  break;
313  }
314  isList = true;
315  }
316 
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 );
319  }
320  uri.setQuery( query );
321 
322  return QString( uri.toEncoded() );
323 
324 }
325 
326 QString QgsMemoryProvider::storageType() const
327 {
328  return QStringLiteral( "Memory storage" );
329 }
330 
331 QgsFeatureIterator QgsMemoryProvider::getFeatures( const QgsFeatureRequest &request ) const
332 {
333  return QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, request ) );
334 }
335 
336 
337 QgsRectangle QgsMemoryProvider::extent() const
338 {
339  if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
340  {
341  mExtent.setMinimal();
342  if ( mSubsetString.isEmpty() )
343  {
344  // fast way - iterate through all features
345  const auto constMFeatures = mFeatures;
346  for ( const QgsFeature &feat : constMFeatures )
347  {
348  if ( feat.hasGeometry() )
349  mExtent.combineExtentWith( feat.geometry().boundingBox() );
350  }
351  }
352  else
353  {
354  QgsFeature f;
355  QgsFeatureIterator fi = getFeatures( QgsFeatureRequest().setNoAttributes() );
356  while ( fi.nextFeature( f ) )
357  {
358  if ( f.hasGeometry() )
359  mExtent.combineExtentWith( f.geometry().boundingBox() );
360  }
361  }
362  }
363  else if ( mFeatures.isEmpty() )
364  {
365  mExtent.setMinimal();
366  }
367 
368  return mExtent;
369 }
370 
371 QgsWkbTypes::Type QgsMemoryProvider::wkbType() const
372 {
373  return mWkbType;
374 }
375 
376 long long QgsMemoryProvider::featureCount() const
377 {
378  if ( mSubsetString.isEmpty() )
379  return mFeatures.count();
380 
381  // subset string set, no alternative but testing each feature
382  QgsFeatureIterator fit = QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, QgsFeatureRequest().setNoAttributes() ) );
383  long long count = 0;
384  QgsFeature feature;
385  while ( fit.nextFeature( feature ) )
386  {
387  count++;
388  }
389  return count;
390 }
391 
392 QgsFields QgsMemoryProvider::fields() const
393 {
394  return mFields;
395 }
396 
397 bool QgsMemoryProvider::isValid() const
398 {
399  return ( mWkbType != QgsWkbTypes::Unknown );
400 }
401 
403 {
404  // TODO: make provider projection-aware
405  return mCrs; // return default CRS
406 }
407 
408 void QgsMemoryProvider::handlePostCloneOperations( QgsVectorDataProvider *source )
409 {
410  if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
411  {
412  // these properties aren't copied when cloning a memory provider by uri, so we need to do it manually
413  mFeatures = other->mFeatures;
414  mNextFeatureId = other->mNextFeatureId;
415  mExtent = other->mExtent;
416  }
417 }
418 
419 // returns TRUE if all features were added successfully, or FALSE if any feature could not be added
420 bool QgsMemoryProvider::addFeatures( QgsFeatureList &flist, Flags flags )
421 {
422  bool result = true;
423  // whether or not to update the layer extent on the fly as we add features
424  const bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
425 
426  const int fieldCount = mFields.count();
427 
428  // For rollback
429  const auto oldExtent { mExtent };
430  const auto oldNextFeatureId { mNextFeatureId };
431  QgsFeatureIds addedFids ;
432 
433  for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result ; ++it )
434  {
435  it->setId( mNextFeatureId );
436  it->setValid( true );
437  if ( it->attributes().count() < fieldCount )
438  {
439  // ensure features have the correct number of attributes by padding
440  // them with null attributes for missing values
441  QgsAttributes attributes = it->attributes();
442  for ( int i = it->attributes().count(); i < mFields.count(); ++i )
443  {
444  attributes.append( QVariant( mFields.at( i ).type() ) );
445  }
446  it->setAttributes( attributes );
447  }
448  else if ( it->attributes().count() > fieldCount )
449  {
450  // too many attributes
451  pushError( tr( "Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( it->attributes().count() ) );
452  QgsAttributes attributes = it->attributes();
453  attributes.resize( mFields.count() );
454  it->setAttributes( attributes );
455  }
456 
457  if ( it->hasGeometry() && mWkbType == QgsWkbTypes::NoGeometry )
458  {
459  it->clearGeometry();
460  }
461  else if ( it->hasGeometry() && QgsWkbTypes::geometryType( it->geometry().wkbType() ) !=
462  QgsWkbTypes::geometryType( mWkbType ) )
463  {
464  pushError( tr( "Could not add feature with geometry type %1 to layer of type %2" ).arg( QgsWkbTypes::displayString( it->geometry().wkbType() ),
465  QgsWkbTypes::displayString( mWkbType ) ) );
466  result = false;
467  continue;
468  }
469 
470  // Check attribute conversion
471  bool conversionError { false };
472  QString errorMessage;
473  for ( int i = 0; i < mFields.count(); ++i )
474  {
475  const QVariant originalValue = it->attribute( i );
476  QVariant attrValue = originalValue;
477  if ( ! attrValue.isNull() && ! mFields.at( i ).convertCompatible( attrValue, &errorMessage ) )
478  {
479  // Push first conversion error only
480  if ( result )
481  {
482  pushError( tr( "Could not store attribute \"%1\": %2" )
483  .arg( mFields.at( i ).name(), errorMessage ) );
484  }
485  result = false;
486  conversionError = true;
487  continue;
488  }
489  else if ( attrValue.type() != originalValue.type() )
490  {
491  // convertCompatible has resulted in a data type conversion
492  it->setAttribute( i, attrValue );
493  }
494  }
495 
496  // Skip the feature if there is at least one conversion error
497  if ( conversionError )
498  {
499  if ( flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
500  {
501  break;
502  }
503  continue;
504  }
505 
506  mFeatures.insert( mNextFeatureId, *it );
507  addedFids.insert( mNextFeatureId );
508 
509  if ( it->hasGeometry() )
510  {
511  if ( updateExtent )
512  mExtent.combineExtentWith( it->geometry().boundingBox() );
513 
514  // update spatial index
515  if ( mSpatialIndex )
516  mSpatialIndex->addFeature( *it );
517  }
518 
519  mNextFeatureId++;
520  }
521 
522  // Roll back
523  if ( ! result && flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
524  {
525  for ( const QgsFeatureId &addedFid : addedFids )
526  {
527  mFeatures.remove( addedFid );
528  }
529  mExtent = oldExtent;
530  mNextFeatureId = oldNextFeatureId;
531  }
532  else
533  {
534  clearMinMaxCache();
535  }
536 
537  return result;
538 }
539 
540 bool QgsMemoryProvider::deleteFeatures( const QgsFeatureIds &id )
541 {
542  for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
543  {
544  const QgsFeatureMap::iterator fit = mFeatures.find( *it );
545 
546  // check whether such feature exists
547  if ( fit == mFeatures.end() )
548  continue;
549 
550  // update spatial index
551  if ( mSpatialIndex )
552  mSpatialIndex->deleteFeature( *fit );
553 
554  mFeatures.erase( fit );
555  }
556 
557  updateExtents();
558  clearMinMaxCache();
559 
560  return true;
561 }
562 
563 bool QgsMemoryProvider::addAttributes( const QList<QgsField> &attributes )
564 {
565  for ( QgsField field : attributes )
566  {
567  if ( !supportedType( field ) )
568  continue;
569 
570  // Make sure added attributes typeName correspond to a native type name
571  bool isNativeTypeName = false;
572  NativeType nativeTypeCandidate( QString(), QString(), QVariant::Invalid );
573  const QList<QgsVectorDataProvider::NativeType> nativeTypesList( nativeTypes() );
574  for ( const NativeType &nativeType : nativeTypesList )
575  {
576  if ( nativeType.mTypeName.toLower() == field.typeName().toLower() )
577  {
578  isNativeTypeName = true;
579  break;
580  }
581 
582  if ( nativeType.mType == field.type()
583  && nativeTypeCandidate.mType == QVariant::Invalid )
584  nativeTypeCandidate = nativeType;
585  }
586  if ( !isNativeTypeName )
587  {
588  if ( nativeTypeCandidate.mType == QVariant::Invalid )
589  {
590  QgsLogger::warning( "Field type not supported: " + field.typeName() );
591  continue;
592  }
593 
594  field.setTypeName( nativeTypeCandidate.mTypeName );
595  }
596 
597  // add new field as a last one
598  mFields.append( field );
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 true;
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 
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:138
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:133
void setTypeName(const QString &typeName)
Set the field type.
Definition: qgsfield.cpp:189
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.
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