QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 <QRegExp>
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  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  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( "bool" ), 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( "doublelist" ), QVariant::List, 0, 0, 0, 0, QVariant::Double )
116 
117  );
118 
119  if ( query.hasQueryItem( QStringLiteral( "field" ) ) )
120  {
121  QList<QgsField> attributes;
122  QRegExp reFieldDef( "\\:"
123  "(int|integer|long|int8|real|double|string|date|time|datetime|binary|bool|boolean)" // type
124  "(?:\\((\\-?\\d+)" // length
125  "(?:\\,(\\-?\\d+))?" // precision
126  "\\))?(\\[\\])?" // array
127  "$", Qt::CaseInsensitive );
128  QStringList fields = query.allQueryItemValues( QStringLiteral( "field" ) );
129  for ( int i = 0; i < fields.size(); i++ )
130  {
131  QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
132  QVariant::Type type = QVariant::String;
133  QVariant::Type subType = QVariant::Invalid;
134  QString typeName( QStringLiteral( "string" ) );
135  int length = 255;
136  int precision = 0;
137 
138  int pos = reFieldDef.indexIn( name );
139  if ( pos >= 0 )
140  {
141  name = name.mid( 0, pos );
142  typeName = reFieldDef.cap( 1 ).toLower();
143  if ( typeName == QLatin1String( "int" ) || typeName == QLatin1String( "integer" ) )
144  {
145  type = QVariant::Int;
146  typeName = QStringLiteral( "integer" );
147  length = -1;
148  }
149  else if ( typeName == QLatin1String( "int8" ) || typeName == QLatin1String( "long" ) )
150  {
151  type = QVariant::LongLong;
152  typeName = QStringLiteral( "int8" );
153  length = -1;
154  }
155  else if ( typeName == QLatin1String( "real" ) || typeName == QLatin1String( "double" ) )
156  {
157  type = QVariant::Double;
158  typeName = QStringLiteral( "double" );
159  length = 20;
160  precision = 5;
161  }
162  else if ( typeName == QLatin1String( "date" ) )
163  {
164  type = QVariant::Date;
165  typeName = QStringLiteral( "date" );
166  length = -1;
167  }
168  else if ( typeName == QLatin1String( "time" ) )
169  {
170  type = QVariant::Time;
171  typeName = QStringLiteral( "time" );
172  length = -1;
173  }
174  else if ( typeName == QLatin1String( "datetime" ) )
175  {
176  type = QVariant::DateTime;
177  typeName = QStringLiteral( "datetime" );
178  length = -1;
179  }
180  else if ( typeName == QLatin1String( "bool" ) || typeName == QLatin1String( "boolean" ) )
181  {
182  type = QVariant::Bool;
183  typeName = QStringLiteral( "boolean" );
184  length = -1;
185  }
186  else if ( typeName == QLatin1String( "binary" ) )
187  {
188  type = QVariant::ByteArray;
189  typeName = QStringLiteral( "binary" );
190  length = -1;
191  }
192 
193  if ( !reFieldDef.cap( 2 ).isEmpty() )
194  {
195  length = reFieldDef.cap( 2 ).toInt();
196  }
197  if ( !reFieldDef.cap( 3 ).isEmpty() )
198  {
199  precision = reFieldDef.cap( 3 ).toInt();
200  }
201  if ( !reFieldDef.cap( 4 ).isEmpty() )
202  {
203  //array
204  subType = type;
205  type = type == QVariant::String ? QVariant::StringList : QVariant::List;
206  typeName += QLatin1String( "list" );
207  }
208  }
209  if ( !name.isEmpty() )
210  attributes.append( QgsField( name, type, typeName, length, precision, QString(), subType ) );
211  }
212  addAttributes( attributes );
213  }
214 
215  if ( query.hasQueryItem( QStringLiteral( "index" ) ) && query.queryItemValue( QStringLiteral( "index" ) ) == QLatin1String( "yes" ) )
216  {
217  createSpatialIndex();
218  }
219 
220 }
221 
222 QgsMemoryProvider::~QgsMemoryProvider()
223 {
224  delete mSpatialIndex;
225 }
226 
227 QString QgsMemoryProvider::providerKey()
228 {
229  return TEXT_PROVIDER_KEY;
230 }
231 
232 QString QgsMemoryProvider::providerDescription()
233 {
234  return TEXT_PROVIDER_DESCRIPTION;
235 }
236 
237 QgsMemoryProvider *QgsMemoryProvider::createProvider( const QString &uri,
238  const ProviderOptions &options,
239  QgsDataProvider::ReadFlags flags )
240 {
241  return new QgsMemoryProvider( uri, options, flags );
242 }
243 
244 QgsAbstractFeatureSource *QgsMemoryProvider::featureSource() const
245 {
246  return new QgsMemoryFeatureSource( this );
247 }
248 
249 QString QgsMemoryProvider::dataSourceUri( bool expandAuthConfig ) const
250 {
251  Q_UNUSED( expandAuthConfig )
252 
253  QUrl uri( QStringLiteral( "memory" ) );
254  QUrlQuery query;
255  QString geometry = QgsWkbTypes::displayString( mWkbType );
256  query.addQueryItem( QStringLiteral( "geometry" ), geometry );
257 
258  if ( mCrs.isValid() )
259  {
260  QString crsDef;
261  QString authid = mCrs.authid();
262  if ( authid.startsWith( QLatin1String( "EPSG:" ) ) )
263  {
264  crsDef = authid;
265  }
266  else
267  {
268  crsDef = QStringLiteral( "wkt:%1" ).arg( mCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) );
269  }
270  query.addQueryItem( QStringLiteral( "crs" ), crsDef );
271  }
272  if ( mSpatialIndex )
273  {
274  query.addQueryItem( QStringLiteral( "index" ), QStringLiteral( "yes" ) );
275  }
276 
277  QgsAttributeList attrs = const_cast<QgsMemoryProvider *>( this )->attributeIndexes();
278  for ( int i = 0; i < attrs.size(); i++ )
279  {
280  const QgsField field = mFields.at( attrs[i] );
281  QString fieldDef = field.name();
282 
283  QString typeName = field.typeName();
284  bool isList = false;
285  if ( field.type() == QVariant::List || field.type() == QVariant::StringList )
286  {
287  switch ( field.subType() )
288  {
289  case QVariant::Int:
290  typeName = QStringLiteral( "integer" );
291  break;
292 
293  case QVariant::LongLong:
294  typeName = QStringLiteral( "long" );
295  break;
296 
297  case QVariant::Double:
298  typeName = QStringLiteral( "double" );
299  break;
300 
301  case QVariant::String:
302  typeName = QStringLiteral( "string" );
303  break;
304 
305  default:
306  break;
307  }
308  isList = true;
309  }
310 
311  fieldDef.append( QStringLiteral( ":%2(%3,%4)%5" ).arg( typeName ).arg( field.length() ).arg( field.precision() ).arg( isList ? QStringLiteral( "[]" ) : QString() ) );
312  query.addQueryItem( QStringLiteral( "field" ), fieldDef );
313  }
314  uri.setQuery( query );
315 
316  return QString( uri.toEncoded() );
317 
318 }
319 
320 QString QgsMemoryProvider::storageType() const
321 {
322  return QStringLiteral( "Memory storage" );
323 }
324 
325 QgsFeatureIterator QgsMemoryProvider::getFeatures( const QgsFeatureRequest &request ) const
326 {
327  return QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, request ) );
328 }
329 
330 
331 QgsRectangle QgsMemoryProvider::extent() const
332 {
333  if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
334  {
335  mExtent.setMinimal();
336  if ( mSubsetString.isEmpty() )
337  {
338  // fast way - iterate through all features
339  const auto constMFeatures = mFeatures;
340  for ( const QgsFeature &feat : constMFeatures )
341  {
342  if ( feat.hasGeometry() )
343  mExtent.combineExtentWith( feat.geometry().boundingBox() );
344  }
345  }
346  else
347  {
348  QgsFeature f;
349  QgsFeatureIterator fi = getFeatures( QgsFeatureRequest().setNoAttributes() );
350  while ( fi.nextFeature( f ) )
351  {
352  if ( f.hasGeometry() )
353  mExtent.combineExtentWith( f.geometry().boundingBox() );
354  }
355  }
356  }
357  else if ( mFeatures.isEmpty() )
358  {
359  mExtent.setMinimal();
360  }
361 
362  return mExtent;
363 }
364 
365 QgsWkbTypes::Type QgsMemoryProvider::wkbType() const
366 {
367  return mWkbType;
368 }
369 
370 long long QgsMemoryProvider::featureCount() const
371 {
372  if ( mSubsetString.isEmpty() )
373  return mFeatures.count();
374 
375  // subset string set, no alternative but testing each feature
376  QgsFeatureIterator fit = QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, QgsFeatureRequest().setNoAttributes() ) );
377  long long count = 0;
378  QgsFeature feature;
379  while ( fit.nextFeature( feature ) )
380  {
381  count++;
382  }
383  return count;
384 }
385 
386 QgsFields QgsMemoryProvider::fields() const
387 {
388  return mFields;
389 }
390 
391 bool QgsMemoryProvider::isValid() const
392 {
393  return ( mWkbType != QgsWkbTypes::Unknown );
394 }
395 
397 {
398  // TODO: make provider projection-aware
399  return mCrs; // return default CRS
400 }
401 
402 void QgsMemoryProvider::handlePostCloneOperations( QgsVectorDataProvider *source )
403 {
404  if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
405  {
406  // these properties aren't copied when cloning a memory provider by uri, so we need to do it manually
407  mFeatures = other->mFeatures;
408  mNextFeatureId = other->mNextFeatureId;
409  mExtent = other->mExtent;
410  }
411 }
412 
413 // returns TRUE if all features were added successfully, or FALSE if any feature could not be added
414 bool QgsMemoryProvider::addFeatures( QgsFeatureList &flist, Flags flags )
415 {
416  bool result = true;
417  // whether or not to update the layer extent on the fly as we add features
418  bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
419 
420  int fieldCount = mFields.count();
421 
422  // For rollback
423  const auto oldExtent { mExtent };
424  const auto oldNextFeatureId { mNextFeatureId };
425  QgsFeatureIds addedFids ;
426 
427  for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result ; ++it )
428  {
429  it->setId( mNextFeatureId );
430  it->setValid( true );
431  if ( it->attributes().count() < fieldCount )
432  {
433  // ensure features have the correct number of attributes by padding
434  // them with null attributes for missing values
435  QgsAttributes attributes = it->attributes();
436  for ( int i = it->attributes().count(); i < mFields.count(); ++i )
437  {
438  attributes.append( QVariant( mFields.at( i ).type() ) );
439  }
440  it->setAttributes( attributes );
441  }
442  else if ( it->attributes().count() > fieldCount )
443  {
444  // too many attributes
445  pushError( tr( "Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( it->attributes().count() ) );
446  QgsAttributes attributes = it->attributes();
447  attributes.resize( mFields.count() );
448  it->setAttributes( attributes );
449  }
450 
451  if ( it->hasGeometry() && mWkbType == QgsWkbTypes::NoGeometry )
452  {
453  it->clearGeometry();
454  }
455  else if ( it->hasGeometry() && QgsWkbTypes::geometryType( it->geometry().wkbType() ) !=
456  QgsWkbTypes::geometryType( mWkbType ) )
457  {
458  pushError( tr( "Could not add feature with geometry type %1 to layer of type %2" ).arg( QgsWkbTypes::displayString( it->geometry().wkbType() ),
459  QgsWkbTypes::displayString( mWkbType ) ) );
460  result = false;
461  continue;
462  }
463 
464  // Check attribute conversion
465  bool conversionError { false };
466  QString errorMessage;
467  for ( int i = 0; i < mFields.count(); ++i )
468  {
469  const QVariant originalValue = it->attribute( i );
470  QVariant attrValue = originalValue;
471  if ( ! attrValue.isNull() && ! mFields.at( i ).convertCompatible( attrValue, &errorMessage ) )
472  {
473  // Push first conversion error only
474  if ( result )
475  {
476  pushError( tr( "Could not store attribute \"%1\": %2" )
477  .arg( mFields.at( i ).name(), errorMessage ) );
478  }
479  result = false;
480  conversionError = true;
481  continue;
482  }
483  else if ( attrValue.type() != originalValue.type() )
484  {
485  // convertCompatible has resulted in a data type conversion
486  it->setAttribute( i, attrValue );
487  }
488  }
489 
490  // Skip the feature if there is at least one conversion error
491  if ( conversionError )
492  {
493  if ( flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
494  {
495  break;
496  }
497  continue;
498  }
499 
500  mFeatures.insert( mNextFeatureId, *it );
501  addedFids.insert( mNextFeatureId );
502 
503  if ( it->hasGeometry() )
504  {
505  if ( updateExtent )
506  mExtent.combineExtentWith( it->geometry().boundingBox() );
507 
508  // update spatial index
509  if ( mSpatialIndex )
510  mSpatialIndex->addFeature( *it );
511  }
512 
513  mNextFeatureId++;
514  }
515 
516  // Roll back
517  if ( ! result && flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
518  {
519  for ( const QgsFeatureId &addedFid : addedFids )
520  {
521  mFeatures.remove( addedFid );
522  }
523  mExtent = oldExtent;
524  mNextFeatureId = oldNextFeatureId;
525  }
526  else
527  {
528  clearMinMaxCache();
529  }
530 
531  return result;
532 }
533 
534 bool QgsMemoryProvider::deleteFeatures( const QgsFeatureIds &id )
535 {
536  for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
537  {
538  QgsFeatureMap::iterator fit = mFeatures.find( *it );
539 
540  // check whether such feature exists
541  if ( fit == mFeatures.end() )
542  continue;
543 
544  // update spatial index
545  if ( mSpatialIndex )
546  mSpatialIndex->deleteFeature( *fit );
547 
548  mFeatures.erase( fit );
549  }
550 
551  updateExtents();
552  clearMinMaxCache();
553 
554  return true;
555 }
556 
557 bool QgsMemoryProvider::addAttributes( const QList<QgsField> &attributes )
558 {
559  for ( QList<QgsField>::const_iterator it = attributes.begin(); it != attributes.end(); ++it )
560  {
561  switch ( it->type() )
562  {
563  case QVariant::Int:
564  case QVariant::Double:
565  case QVariant::String:
566  case QVariant::Date:
567  case QVariant::Time:
568  case QVariant::DateTime:
569  case QVariant::LongLong:
570  case QVariant::StringList:
571  case QVariant::List:
572  case QVariant::Bool:
573  case QVariant::ByteArray:
574  break;
575  default:
576  QgsDebugMsg( "Field type not supported: " + it->typeName() );
577  continue;
578  }
579  // add new field as a last one
580  mFields.append( *it );
581 
582  for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
583  {
584  QgsFeature &f = fit.value();
585  QgsAttributes attr = f.attributes();
586  attr.append( QVariant() );
587  f.setAttributes( attr );
588  }
589  }
590  return true;
591 }
592 
593 bool QgsMemoryProvider::renameAttributes( const QgsFieldNameMap &renamedAttributes )
594 {
595  QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
596  bool result = true;
597  for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
598  {
599  int fieldIndex = renameIt.key();
600  if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
601  {
602  result = false;
603  continue;
604  }
605  if ( mFields.indexFromName( renameIt.value() ) >= 0 )
606  {
607  //field name already in use
608  result = false;
609  continue;
610  }
611 
612  mFields.rename( fieldIndex, renameIt.value() );
613  }
614  return result;
615 }
616 
617 bool QgsMemoryProvider::deleteAttributes( const QgsAttributeIds &attributes )
618 {
619  QList<int> attrIdx = qgis::setToList( attributes );
620  std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
621 
622  // delete attributes one-by-one with decreasing index
623  for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
624  {
625  int idx = *it;
626  mFields.remove( idx );
627 
628  for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
629  {
630  QgsFeature &f = fit.value();
631  QgsAttributes attr = f.attributes();
632  attr.remove( idx );
633  f.setAttributes( attr );
634  }
635  }
636  clearMinMaxCache();
637  return true;
638 }
639 
640 bool QgsMemoryProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
641 {
642  bool result { true };
643 
644  QgsChangedAttributesMap rollBackMap;
645 
646  QString errorMessage;
647  for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
648  {
649  QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
650  if ( fit == mFeatures.end() )
651  continue;
652 
653  const QgsAttributeMap &attrs = it.value();
654  QgsAttributeMap rollBackAttrs;
655 
656  // Break on errors
657  for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
658  {
659  QVariant attrValue = it2.value();
660  // Check attribute conversion
661  const bool conversionError { ! attrValue.isNull()
662  && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
663  if ( conversionError )
664  {
665  // Push first conversion error only
666  if ( result )
667  {
668  pushError( tr( "Could not change attribute %1 having type %2 for feature %4: %3" )
669  .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
670  errorMessage ).arg( it.key() ) );
671  }
672  result = false;
673  break;
674  }
675  rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
676  fit->setAttribute( it2.key(), attrValue );
677  }
678  rollBackMap.insert( it.key(), rollBackAttrs );
679  }
680 
681  // Roll back
682  if ( ! result )
683  {
684  changeAttributeValues( rollBackMap );
685  }
686  else
687  {
688  clearMinMaxCache();
689  }
690  return result;
691 }
692 
693 bool QgsMemoryProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
694 {
695  for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
696  {
697  QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
698  if ( fit == mFeatures.end() )
699  continue;
700 
701  // update spatial index
702  if ( mSpatialIndex )
703  mSpatialIndex->deleteFeature( *fit );
704 
705  fit->setGeometry( it.value() );
706 
707  // update spatial index
708  if ( mSpatialIndex )
709  mSpatialIndex->addFeature( *fit );
710  }
711 
712  updateExtents();
713 
714  return true;
715 }
716 
717 QString QgsMemoryProvider::subsetString() const
718 {
719  return mSubsetString;
720 }
721 
722 bool QgsMemoryProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount )
723 {
724  Q_UNUSED( updateFeatureCount )
725 
726  if ( !theSQL.isEmpty() )
727  {
728  QgsExpression tempExpression( theSQL );
729  if ( tempExpression.hasParserError() )
730  return false;
731  }
732 
733  if ( theSQL == mSubsetString )
734  return true;
735 
736  mSubsetString = theSQL;
737  clearMinMaxCache();
738  mExtent.setMinimal();
739 
740  emit dataChanged();
741  return true;
742 }
743 
744 bool QgsMemoryProvider::createSpatialIndex()
745 {
746  if ( !mSpatialIndex )
747  {
748  mSpatialIndex = new QgsSpatialIndex();
749 
750  // add existing features to index
751  for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
752  {
753  mSpatialIndex->addFeature( *it );
754  }
755  }
756  return true;
757 }
758 
759 QgsFeatureSource::SpatialIndexPresence QgsMemoryProvider::hasSpatialIndex() const
760 {
761  return mSpatialIndex ? SpatialIndexPresent : SpatialIndexNotPresent;
762 }
763 
764 QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities() const
765 {
766  return AddFeatures | DeleteFeatures | ChangeGeometries |
767  ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
768  SelectAtId | CircularGeometries | FastTruncate;
769 }
770 
771 bool QgsMemoryProvider::truncate()
772 {
773  mFeatures.clear();
774  clearMinMaxCache();
775  mExtent.setMinimal();
776  return true;
777 }
778 
779 void QgsMemoryProvider::updateExtents()
780 {
781  mExtent.setMinimal();
782 }
783 
784 QString QgsMemoryProvider::name() const
785 {
786  return TEXT_PROVIDER_KEY;
787 }
788 
789 QString QgsMemoryProvider::description() const
790 {
791  return TEXT_PROVIDER_DESCRIPTION;
792 }
793 
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:135
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:205
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
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
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:938
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:731
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
Definition: qgsfeature.h:722
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:736
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
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QSet< int > QgsAttributeIds
const QgsCoordinateReferenceSystem & crs
const QString & typeName
int precision
const QgsAttributeList & attributeIndexes