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