QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgsvectorlayerfeatureiterator.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayerfeatureiterator.cpp
3  ---------------------
4  begin : Dezember 2012
5  copyright : (C) 2012 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  ***************************************************************************/
16 
18 #include "qgsgeometrysimplifier.h"
19 #include "qgssimplifymethod.h"
20 #include "qgsvectordataprovider.h"
22 #include "qgsvectorlayer.h"
24 #include "qgsexpressioncontext.h"
25 #include "qgsdistancearea.h"
26 #include "qgsproject.h"
27 #include "qgsmessagelog.h"
28 #include "qgsexception.h"
30 #include "qgsgeometryengine.h"
31 
32 #include <QThreadStorage>
33 #include <QStack>
34 
36 {
37  const QMutexLocker locker( &layer->mFeatureSourceConstructorMutex );
38  const QgsVectorDataProvider *provider = layer->dataProvider();
39  if ( provider )
40  mProviderFeatureSource.reset( provider->featureSource() );
41  mFields = layer->fields();
42  mId = layer->id();
43 
44  // update layer's join caches if necessary
45  if ( layer->mJoinBuffer->containsJoins() )
46  layer->mJoinBuffer->createJoinCaches();
47 
48  mJoinBuffer.reset( layer->mJoinBuffer->clone() );
49 
50  mExpressionFieldBuffer.reset( new QgsExpressionFieldBuffer( *layer->mExpressionFieldBuffer ) );
51  mCrs = layer->crs();
52 
53  mHasEditBuffer = layer->editBuffer();
54  if ( mHasEditBuffer )
55  {
56 #if 0
57  // TODO[MD]: after merge
58  if ( request.filterType() == QgsFeatureRequest::FilterFid )
59  {
60 
61  // only copy relevant parts
62  if ( L->editBuffer()->addedFeatures().contains( request.filterFid() ) )
63  mAddedFeatures.insert( request.filterFid(), L->editBuffer()->addedFeatures()[ request.filterFid()] );
64 
65  if ( L->editBuffer()->changedGeometries().contains( request.filterFid() ) )
66  mChangedGeometries.insert( request.filterFid(), L->editBuffer()->changedGeometries()[ request.filterFid()] );
67 
68  if ( L->editBuffer()->deletedFeatureIds().contains( request.filterFid() ) )
69  mDeletedFeatureIds.insert( request.filterFid() );
70 
71  if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
72  mChangedAttributeValues.insert( request.filterFid(), L->editBuffer()->changedAttributeValues()[ request.filterFid()] );
73 
74  if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
75  mChangedFeaturesRequest.setFilterFids( QgsFeatureIds() << request.filterFid() );
76  }
77  else
78  {
79 #endif
80  // If we are inside a transaction the iterator "sees" the current status
81  if ( provider && ! provider->transaction() )
82  {
87  mAddedAttributes = QList<QgsField>( layer->editBuffer()->addedAttributes() );
89  }
90 #if 0
91  }
92 #endif
93  }
94 
95  const std::unique_ptr< QgsExpressionContextScope > layerScope( QgsExpressionContextUtils::layerScope( layer ) );
96  mLayerScope = *layerScope;
97 }
98 
100 
102 {
103  // return feature iterator that does not own this source
104  return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, false, request ) );
105 }
106 
108 {
109  return mFields;
110 }
111 
113 {
114  return mCrs;
115 }
116 
118 {
119  return mId;
120 }
121 
122 
125  , mFetchedFid( false )
126 {
128  {
130  }
131 
132  // prepare spatial filter geometries for optimal speed
133  // since the mDistanceWithin* constraint member variables are all in the DESTINATION CRS,
134  // we set all these upfront before any transformation to the source CRS is done.
135 
136  switch ( mRequest.spatialFilterType() )
137  {
140  break;
141 
144  {
145  // Note that regardless of whether or not we'll ultimately be able to handoff this check to the underlying provider,
146  // we still need these reference geometry constraints in the vector layer iterator as we need them to check against
147  // the features from the vector layer's edit buffer! (In other words, we cannot completely hand off responsibility for
148  // these checks to the provider and ignore them locally)
151  mDistanceWithinEngine->prepareGeometry();
153  }
154  break;
155  }
156 
157  bool canDelegateLimitToProvider = true;
158  try
159  {
161  {
163  break;
164 
166  // we have to disable any limit on the provider's request -- since that request may be returning features which are outside the
167  // distance tolerance, we'll have to fetch them all and then handle the limit check manually only after testing for the distance within constraint
168  canDelegateLimitToProvider = false;
169  break;
170  }
171 
172  // mFilterRect is in the source CRS, so we set that now (after request transformation has been done)
174  }
175  catch ( QgsCsException & )
176  {
177  // can't reproject request filters
178  close();
179  return;
180  }
181 
182  // check whether the order by clause(s) can be delegated to the provider
183  mDelegatedOrderByToProvider = !mSource->mHasEditBuffer;
184  if ( !mRequest.orderBy().isEmpty() )
185  {
186  QSet<int> attributeIndexes;
187  const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields );
188  for ( const int attrIndex : usedAttributeIndices )
189  {
190  if ( mSource->mFields.fieldOrigin( attrIndex ) != QgsFields::OriginProvider )
191  mDelegatedOrderByToProvider = false;
192 
193  attributeIndexes << attrIndex;
194  }
195 
196  if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mDelegatedOrderByToProvider )
197  {
198  attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
199  mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
200  }
201  }
202 
204  {
207 
209  {
210  // ensure that all fields required for filter expressions are prepared
212  attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
213  mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
214  }
215  }
216 
217  prepareFields();
218 
219  mHasVirtualAttributes = !mFetchJoinInfo.isEmpty() || !mExpressionFieldInfo.isEmpty();
220 
221  // by default provider's request is the same
223  // but we remove any destination CRS parameter - that is handled in QgsVectorLayerFeatureIterator,
224  // not at the provider level. Otherwise virtual fields depending on geometry would have incorrect
225  // values
226  if ( mRequest.destinationCrs().isValid() )
227  {
229  }
230 
231  if ( !mDelegatedOrderByToProvider )
232  {
234  }
235 
236  if ( !canDelegateLimitToProvider )
237  {
239  }
240 
242  {
243  // prepare list of attributes to match provider fields
244  QSet<int> providerSubset;
246  const int nPendingFields = mSource->mFields.count();
247  for ( const int attrIndex : subset )
248  {
249  if ( attrIndex < 0 || attrIndex >= nPendingFields )
250  continue;
251  if ( mSource->mFields.fieldOrigin( attrIndex ) == QgsFields::OriginProvider )
252  providerSubset << mSource->mFields.fieldOriginIndex( attrIndex );
253  }
254 
255  // This is done in order to be prepared to do fallback order bys
256  // and be sure we have the required columns.
257  // TODO:
258  // It would be nicer to first check if we can compile the order by
259  // and only modify the subset if we cannot.
260  if ( !mProviderRequest.orderBy().isEmpty() )
261  {
262  const auto usedAttributeIndices = mProviderRequest.orderBy().usedAttributeIndices( mSource->mFields );
263  for ( const int attrIndex : usedAttributeIndices )
264  {
265  providerSubset << attrIndex;
266  }
267  }
268 
269  mProviderRequest.setSubsetOfAttributes( qgis::setToList( providerSubset ) );
270  }
271 
273  {
274  const bool needsGeom = mProviderRequest.filterExpression()->needsGeometry();
275  const auto constReferencedColumns = mProviderRequest.filterExpression()->referencedColumns();
276  for ( const QString &field : constReferencedColumns )
277  {
278  const int idx = source->mFields.lookupField( field );
279 
280  // If there are fields in the expression which are not of origin provider, the provider will not be able to filter based on them.
281  // In this case we disable the expression filter.
282  if ( source->mFields.fieldOrigin( idx ) != QgsFields::OriginProvider )
283  {
285  // can't limit at provider side
287  if ( needsGeom )
288  {
289  // have to get geometry from provider in order to evaluate expression on client
291  }
292  break;
293  }
294  }
295  }
296 
297  if ( mSource->mHasEditBuffer )
298  {
300  QgsFeatureIds changedIds;
301  QgsChangedAttributesMap::const_iterator attIt = mSource->mChangedAttributeValues.constBegin();
302  for ( ; attIt != mSource->mChangedAttributeValues.constEnd(); ++attIt )
303  {
304  changedIds << attIt.key();
305  }
307 
308  if ( mChangedFeaturesRequest.limit() > 0 )
309  {
310  int providerLimit = mProviderRequest.limit();
311 
312  // features may be deleted in buffer, so increase limit sent to provider
313  providerLimit += mSource->mDeletedFeatureIds.size();
314 
316  {
317  // attribute changes may mean some features no longer match expression, so increase limit sent to provider
318  providerLimit += mSource->mChangedAttributeValues.size();
319  }
320 
322  {
323  // geometry changes may mean some features no longer match expression or rect, so increase limit sent to provider
324  providerLimit += mSource->mChangedGeometries.size();
325  }
326 
327  mProviderRequest.setLimit( providerLimit );
328  mChangedFeaturesRequest.setLimit( providerLimit );
329  }
330  }
331 
332  if ( request.filterType() == QgsFeatureRequest::FilterFid )
333  {
334  mFetchedFid = false;
335  }
336  else // no filter or filter by rect
337  {
339  {
340  if ( mSource->mHasEditBuffer )
341  {
343  }
344  else
345  {
347  }
348  }
349 
351  }
352 }
353 
354 
356 {
357  qDeleteAll( mExpressionFieldInfo );
358 
359  close();
360 }
361 
363 
369 class QgsThreadStackOverflowGuard
370 {
371  public:
372 
373  QgsThreadStackOverflowGuard( QThreadStorage<QStack<QString>> &storage, const QString &stackFrameInformation, int maxDepth )
374  : mStorage( storage )
375  , mMaxDepth( maxDepth )
376  {
377  if ( !storage.hasLocalData() )
378  {
379  storage.setLocalData( QStack<QString>() );
380  }
381 
382  storage.localData().push( stackFrameInformation );
383  }
384 
385  ~QgsThreadStackOverflowGuard()
386  {
387  mStorage.localData().pop();
388  }
389 
390  bool hasStackOverflow() const
391  {
392  if ( mStorage.localData().size() > mMaxDepth )
393  return true;
394  else
395  return false;
396  }
397 
398  QString topFrames() const
399  {
400  QStringList dumpStack;
401  const QStack<QString> &stack = mStorage.localData();
402 
403  const int dumpSize = std::min( static_cast<int>( stack.size() ), 10 );
404  for ( int i = 0; i < dumpSize; ++i )
405  {
406  dumpStack += stack.at( i );
407  }
408 
409  return dumpStack.join( '\n' );
410  }
411 
412  int depth() const
413  {
414  return mStorage.localData().size();
415  }
416 
417  private:
418  QThreadStorage<QStack<QString>> &mStorage;
419  int mMaxDepth;
420 };
421 
423 
425 {
426  f.setValid( false );
427 
428  if ( mClosed )
429  return false;
430 
431  static QThreadStorage<QStack<QString>> sStack;
432 
433  const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
434 
435  if ( guard.hasStackOverflow() )
436  {
437  QgsMessageLog::logMessage( QObject::tr( "Stack overflow, too many nested feature iterators.\nIterated layers:\n%3\n..." ).arg( mSource->id(), guard.topFrames() ), QObject::tr( "General" ), Qgis::MessageLevel::Critical );
438  return false;
439  }
440 
442  {
443  if ( mFetchedFid )
444  return false;
445  const bool res = nextFeatureFid( f );
446  if ( res && postProcessFeature( f ) )
447  {
448  mFetchedFid = true;
449  return res;
450  }
451  else
452  {
453  return false;
454  }
455  }
456 
457  if ( !mFilterRect.isNull() )
458  {
459  if ( fetchNextChangedGeomFeature( f ) )
460  return true;
461 
462  // no more changed geometries
463  }
464 
466  {
468  return true;
469 
470  if ( fetchNextChangedGeomFeature( f ) )
471  return true;
472 
473  // no more changed features
474  }
475 
476  while ( fetchNextAddedFeature( f ) )
477  {
478  return true;
479  }
480  // no more added features
481 
482  if ( mProviderIterator.isClosed() )
483  {
486  {
488  mProviderIterator.setInterruptionChecker( mInterruptionChecker );
489  }
490  }
491 
492  while ( mProviderIterator.nextFeature( f ) )
493  {
494  if ( mFetchConsidered.contains( f.id() ) )
495  continue;
496 
497  // TODO[MD]: just one resize of attributes
498  f.setFields( mSource->mFields );
499 
500  // update attributes
501  if ( mSource->mHasEditBuffer )
503 
504  if ( mHasVirtualAttributes )
506 
508  {
509  //filtering by expression, and couldn't do it on the provider side
512  {
513  //feature did not match filter
514  continue;
515  }
516  }
517 
518  // update geometry
519  // TODO[MK]: FilterRect check after updating the geometry
522 
523  if ( !postProcessFeature( f ) )
524  continue;
525 
526  return true;
527  }
528  // no more provider features
529 
530  close();
531  return false;
532 }
533 
534 
535 
537 {
538  if ( mClosed )
539  return false;
540 
542  {
543  mFetchedFid = false;
544  }
545  else
546  {
549  }
550 
551  return true;
552 }
553 
555 {
556  if ( mClosed )
557  return false;
558 
560 
561  iteratorClosed();
562 
563  mClosed = true;
564  return true;
565 }
566 
568 {
569  mProviderIterator.setInterruptionChecker( interruptionChecker );
570  mInterruptionChecker = interruptionChecker;
571 }
572 
574 {
575  return mProviderIterator.isValid();
576 }
577 
579 {
580  while ( mFetchAddedFeaturesIt != mSource->mAddedFeatures.constBegin() )
581  {
583  const QgsFeatureId fid = mFetchAddedFeaturesIt->id();
584 
585  if ( mFetchConsidered.contains( fid ) )
586  // must have changed geometry outside rectangle
587  continue;
588 
590 
591  // can't test for feature acceptance until after calling useAddedFeature
592  // since acceptFeature may rely on virtual fields
593  if ( !mRequest.acceptFeature( f ) )
594  // skip features which are not accepted by the filter
595  continue;
596 
597  if ( !postProcessFeature( f ) )
598  continue;
599 
600  return true;
601  }
602 
604  return false; // no more added features
605 }
606 
607 
609 {
610  // since QgsFeature is implicitly shared, it's more efficient to just copy the
611  // whole feature, even if flags like NoGeometry or a subset of attributes is set at the request.
612  // This helps potentially avoid an unnecessary detach of the feature
613  f = src;
614  f.setValid( true );
615  f.setFields( mSource->mFields );
616 
617  if ( mHasVirtualAttributes )
619 }
620 
621 
622 
624 {
625  // check if changed geometries are in rectangle
627  {
628  const QgsFeatureId fid = mFetchChangedGeomIt.key();
629 
630  if ( mFetchConsidered.contains( fid ) )
631  // skip deleted features
632  continue;
633 
634  mFetchConsidered << fid;
635 
636  if ( !mFilterRect.isNull() && !mFetchChangedGeomIt->intersects( mFilterRect ) )
637  // skip changed geometries not in rectangle and don't check again
638  continue;
639 
641 
643  {
646  {
647  continue;
648  }
649  }
650 
651  if ( postProcessFeature( f ) )
652  {
653  // return complete feature
655  return true;
656  }
657  }
658 
659  return false; // no more changed geometries
660 }
661 
663 {
665  {
666  if ( mFetchConsidered.contains( f.id() ) )
667  continue;
668 
669  mFetchConsidered << f.id();
670 
672 
673  // also update geometry if needed
674  const auto changedGeometryIt = mSource->mChangedGeometries.constFind( f.id() );
675  if ( changedGeometryIt != mSource->mChangedGeometries.constEnd() )
676  {
677  if ( !mFilterRect.isNull() && !changedGeometryIt->intersects( mFilterRect ) )
678  // skip changed geometries not in rectangle and don't check again
679  continue;
680 
681  f.setGeometry( *changedGeometryIt );
682  }
683 
684  if ( mHasVirtualAttributes )
686 
688  if ( mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() && postProcessFeature( f ) )
689  {
690  return true;
691  }
692  }
693 
694  return false;
695 }
696 
697 
699 {
700  f.setId( fid );
701  f.setValid( true );
702  f.setFields( mSource->mFields );
703 
706  {
707  f.setGeometry( geom );
708  }
709 
710  const bool subsetAttrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes );
711  if ( !subsetAttrs || !mRequest.subsetOfAttributes().isEmpty() )
712  {
713  // retrieve attributes from provider
714  QgsFeature tmp;
715  //mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes );
716  QgsFeatureRequest request;
718  if ( subsetAttrs )
719  {
721  }
722  QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( request );
723  if ( fi.nextFeature( tmp ) )
724  {
727  f.setAttributes( tmp.attributes() );
728  }
729  }
730 
732 }
733 
734 
735 
737 {
739 
742 }
743 
745 {
746  if ( !mSource->mFields.exists( fieldIdx ) )
747  return;
748 
749  if ( mSource->mFields.fieldOrigin( fieldIdx ) != QgsFields::OriginJoin )
750  return;
751 
752  int sourceLayerIndex;
753  const QgsVectorLayerJoinInfo *joinInfo = mSource->mJoinBuffer->joinForFieldIndex( fieldIdx, mSource->mFields, sourceLayerIndex );
754  Q_ASSERT( joinInfo );
755 
756  QgsVectorLayer *joinLayer = joinInfo->joinLayer();
757  if ( !joinLayer )
758  return; // invalid join (unresolved reference to layer)
759 
760  if ( !mFetchJoinInfo.contains( joinInfo ) )
761  {
762  FetchJoinInfo info;
763  info.joinInfo = joinInfo;
764  info.joinSource = std::make_shared< QgsVectorLayerFeatureSource >( joinLayer );
765  info.indexOffset = mSource->mJoinBuffer->joinedFieldsOffset( joinInfo, mSource->mFields );
767  info.joinField = joinLayer->fields().indexFromName( joinInfo->joinFieldName() );
768  info.joinLayerFields = joinLayer->fields();
769 
770  // for joined fields, we always need to request the targetField from the provider too
771  if ( !mPreparedFields.contains( info.targetField ) && !mFieldsToPrepare.contains( info.targetField ) )
772  mFieldsToPrepare << info.targetField;
773 
776 
777  mFetchJoinInfo.insert( joinInfo, info );
778  }
779 
780  // store field source index - we'll need it when fetching from provider
781  mFetchJoinInfo[ joinInfo ].attributes.push_back( sourceLayerIndex );
782  mFetchJoinInfo[ joinInfo ].attributesSourceToDestLayerMap[sourceLayerIndex] = fieldIdx;
783 }
784 
785 
787 {
788  static QThreadStorage<QStack<QString>> sStack;
789 
790  const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
791 
792  if ( guard.hasStackOverflow() )
793  {
794  QgsMessageLog::logMessage( QObject::tr( "Stack overflow when preparing field %1 of layer %2.\nLast frames:\n%3\n..." ).arg( mSource->fields().at( fieldIdx ).name(), mSource->id(), guard.topFrames() ), QObject::tr( "General" ), Qgis::MessageLevel::Critical );
795  return;
796  }
797 
798  const QList<QgsExpressionFieldBuffer::ExpressionField> &exps = mSource->mExpressionFieldBuffer->expressions();
799 
800  const int oi = mSource->mFields.fieldOriginIndex( fieldIdx );
801  std::unique_ptr<QgsExpression> exp = std::make_unique<QgsExpression>( exps[oi].cachedExpression );
802 
803  QgsDistanceArea da;
805  da.setEllipsoid( QgsProject::instance()->ellipsoid() );
806  exp->setGeomCalculator( &da );
807  exp->setDistanceUnits( QgsProject::instance()->distanceUnits() );
808  exp->setAreaUnits( QgsProject::instance()->areaUnits() );
809 
810  if ( !mExpressionContext )
811  createExpressionContext();
812  exp->prepare( mExpressionContext.get() );
813  const QSet<int> referencedColumns = exp->referencedAttributeIndexes( mSource->fields() );
814 
815  QSet<int> requestedAttributes = qgis::listToSet( mRequest.subsetOfAttributes() );
816 
817  for ( const int dependentFieldIdx : referencedColumns )
818  {
820  {
821  requestedAttributes += dependentFieldIdx;
822  }
823  // also need to fetch this dependent field
824  if ( !mPreparedFields.contains( dependentFieldIdx ) && !mFieldsToPrepare.contains( dependentFieldIdx ) )
825  mFieldsToPrepare << dependentFieldIdx;
826  }
827 
829  {
830  mRequest.setSubsetOfAttributes( qgis::setToList( requestedAttributes ) );
831  }
832 
833  if ( exp->needsGeometry() )
834  {
836  }
837 
838  mExpressionFieldInfo.insert( fieldIdx, exp.release() );
839 }
840 
842 {
843  mPreparedFields.clear();
844  mFieldsToPrepare.clear();
845  mFetchJoinInfo.clear();
846  mOrderedJoinInfoList.clear();
847 
848  mExpressionContext.reset();
849 
850  mFieldsToPrepare = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
853 
854  while ( !mFieldsToPrepare.isEmpty() )
855  {
856  const int fieldIdx = mFieldsToPrepare.takeFirst();
857  if ( mPreparedFields.contains( fieldIdx ) )
858  continue;
859 
860  mPreparedFields << fieldIdx;
861  prepareField( fieldIdx );
862  }
863 
864  //sort joins by dependency
865  if ( !mFetchJoinInfo.empty() )
866  {
867  createOrderedJoinList();
868  }
869 }
870 
871 void QgsVectorLayerFeatureIterator::createOrderedJoinList()
872 {
873  mOrderedJoinInfoList = mFetchJoinInfo.values();
874  if ( mOrderedJoinInfoList.size() < 2 )
875  {
876  return;
877  }
878 
879  QSet<int> resolvedFields; //todo: get provider / virtual fields without joins
880 
881  //add all provider fields without joins as resolved fields
882  QList< int >::const_iterator prepFieldIt = mPreparedFields.constBegin();
883  for ( ; prepFieldIt != mPreparedFields.constEnd(); ++prepFieldIt )
884  {
885  if ( mSource->mFields.fieldOrigin( *prepFieldIt ) != QgsFields::OriginJoin )
886  {
887  resolvedFields.insert( *prepFieldIt );
888  }
889  }
890 
891  //iterate through the joins. If target field is not yet covered, move the entry to the end of the list
892 
893  //some join combinations might not have a resolution at all
894  const int maxIterations = ( mOrderedJoinInfoList.size() + 1 ) * mOrderedJoinInfoList.size() / 2.0;
895  int currentIteration = 0;
896 
897  for ( int i = 0; i < mOrderedJoinInfoList.size() - 1; ++i )
898  {
899  if ( !resolvedFields.contains( mOrderedJoinInfoList.at( i ).targetField ) )
900  {
901  mOrderedJoinInfoList.append( mOrderedJoinInfoList.at( i ) );
902  mOrderedJoinInfoList.removeAt( i );
903  --i;
904  }
905  else
906  {
907  const int offset = mOrderedJoinInfoList.at( i ).indexOffset;
908  const int joinField = mOrderedJoinInfoList.at( i ).joinField;
909 
910  const QgsAttributeList attributes = mOrderedJoinInfoList.at( i ).attributes;
911  for ( int n = 0; n < attributes.size(); n++ )
912  {
913  if ( n != joinField )
914  {
915  resolvedFields.insert( joinField < n ? n + offset - 1 : n + offset );
916  }
917  }
918  }
919 
920  ++currentIteration;
921  if ( currentIteration >= maxIterations )
922  {
923  break;
924  }
925  }
926 }
927 
928 bool QgsVectorLayerFeatureIterator::postProcessFeature( QgsFeature &feature )
929 {
930  bool result = checkGeometryValidity( feature );
931  if ( result )
933 
934  if ( result && mDistanceWithinEngine && feature.hasGeometry() )
935  {
936  result = mDistanceWithinEngine->distanceWithin( feature.geometry().constGet(), mDistanceWithin );
937  }
938 
939  return result;
940 }
941 
942 bool QgsVectorLayerFeatureIterator::checkGeometryValidity( const QgsFeature &feature )
943 {
944  if ( !feature.hasGeometry() )
945  return true;
946 
947  switch ( mRequest.invalidGeometryCheck() )
948  {
950  return true;
951 
953  {
954  if ( !feature.geometry().isGeosValid() )
955  {
956  QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
958  {
959  mRequest.invalidGeometryCallback()( feature );
960  }
961  return false;
962  }
963  break;
964  }
965 
967  if ( !feature.geometry().isGeosValid() )
968  {
969  QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
970  close();
972  {
973  mRequest.invalidGeometryCallback()( feature );
974  }
975  return false;
976  }
977  break;
978  }
979 
980  return true;
981 }
982 
984 {
985  switch ( mSource->mFields.fieldOrigin( fieldIdx ) )
986  {
988  prepareExpression( fieldIdx );
989  break;
990 
992  if ( mSource->mJoinBuffer->containsJoins() )
993  {
994  prepareJoin( fieldIdx );
995  }
996  break;
997 
1000  case QgsFields::OriginEdit:
1001  break;
1002  }
1003 }
1004 
1006 {
1007  QList< FetchJoinInfo >::const_iterator joinIt = mOrderedJoinInfoList.constBegin();
1008  for ( ; joinIt != mOrderedJoinInfoList.constEnd(); ++joinIt )
1009  {
1010  const QVariant targetFieldValue = f.attribute( joinIt->targetField );
1011  if ( !targetFieldValue.isValid() )
1012  continue;
1013 
1014  const QHash< QString, QgsAttributes> &memoryCache = joinIt->joinInfo->cachedAttributes;
1015  if ( memoryCache.isEmpty() )
1016  joinIt->addJoinedAttributesDirect( f, targetFieldValue );
1017  else
1018  joinIt->addJoinedAttributesCached( f, targetFieldValue );
1019  }
1020 }
1021 
1023 {
1024  // make sure we have space for newly added attributes
1025  QgsAttributes attr = f.attributes();
1026  attr.resize( mSource->mFields.count() ); // Provider attrs count + joined attrs count + expression attrs count
1027  f.setAttributes( attr );
1028 
1029  // possible TODO - handle combinations of expression -> join -> expression -> join?
1030  // but for now, write that off as too complex and an unlikely rare, unsupported use case
1031 
1032  QList< int > fetchedVirtualAttributes;
1033  //first, check through joins for any virtual fields we need
1034  QMap<const QgsVectorLayerJoinInfo *, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
1035  for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
1036  {
1037  if ( mExpressionFieldInfo.contains( joinIt->targetField ) )
1038  {
1039  // have to calculate expression field before we can handle this join
1040  addExpressionAttribute( f, joinIt->targetField );
1041  fetchedVirtualAttributes << joinIt->targetField;
1042  }
1043  }
1044 
1045  if ( !mFetchJoinInfo.isEmpty() )
1046  addJoinedAttributes( f );
1047 
1048  // add remaining expression fields
1049  if ( !mExpressionFieldInfo.isEmpty() )
1050  {
1051  QMap<int, QgsExpression *>::ConstIterator it = mExpressionFieldInfo.constBegin();
1052  for ( ; it != mExpressionFieldInfo.constEnd(); ++it )
1053  {
1054  if ( fetchedVirtualAttributes.contains( it.key() ) )
1055  continue;
1056 
1057  addExpressionAttribute( f, it.key() );
1058  }
1059  }
1060 }
1061 
1063 {
1064  QgsExpression *exp = mExpressionFieldInfo.value( attrIndex );
1065  if ( exp )
1066  {
1067  if ( !mExpressionContext )
1068  createExpressionContext();
1069 
1070  mExpressionContext->setFeature( f );
1071  QVariant val = exp->evaluate( mExpressionContext.get() );
1072  ( void )mSource->mFields.at( attrIndex ).convertCompatible( val );
1073  f.setAttribute( attrIndex, val );
1074  }
1075  else
1076  {
1077  f.setAttribute( attrIndex, QVariant() );
1078  }
1079 }
1080 
1082 {
1083  Q_UNUSED( simplifyMethod )
1084  return false;
1085 }
1086 
1087 bool QgsVectorLayerFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
1088 {
1089  Q_UNUSED( methodType )
1090  return false;
1091 }
1092 
1093 
1095 {
1096  const QHash<QString, QgsAttributes> &memoryCache = joinInfo->cachedAttributes;
1097  const QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
1098  if ( it == memoryCache.constEnd() )
1099  return; // joined value not found -> leaving the attributes empty (null)
1100 
1101  int index = indexOffset;
1102 
1103  const QgsAttributes &featureAttributes = it.value();
1104  for ( int i = 0; i < featureAttributes.count(); ++i )
1105  {
1106  f.setAttribute( index++, featureAttributes.at( i ) );
1107  }
1108 }
1109 
1110 
1111 
1113 {
1114 #if 0 // this is not thread safe -- we cannot access the layer here as this will be called from non-main threads.
1115  // Shortcut
1116  if ( joinLayer && ! joinLayer->hasFeatures() )
1117  {
1118  return;
1119  }
1120 #endif
1121 
1122  // no memory cache, query the joined values by setting substring
1123  QString subsetString;
1124 
1125  const QString joinFieldName = joinInfo->joinFieldName();
1126 
1127  subsetString.append( QStringLiteral( "\"%1\"" ).arg( joinFieldName ) );
1128 
1129  if ( joinValue.isNull() )
1130  {
1131  subsetString += QLatin1String( " IS NULL" );
1132  }
1133  else
1134  {
1135  QString v = joinValue.toString();
1136  switch ( joinValue.type() )
1137  {
1138  case QVariant::Int:
1139  case QVariant::LongLong:
1140  case QVariant::Double:
1141  break;
1142 
1143  default:
1144  case QVariant::String:
1145  v.replace( '\'', QLatin1String( "''" ) );
1146  v.prepend( '\'' ).append( '\'' );
1147  break;
1148  }
1149  subsetString += '=' + v;
1150  }
1151 
1152  QList<int> joinedAttributeIndices;
1153 
1154  // maybe user requested just a subset of layer's attributes
1155  // so we do not have to cache everything
1156  if ( joinInfo->hasSubset() )
1157  {
1158  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinInfo );
1159  const QVector<int> subsetIndices = QgsVectorLayerJoinBuffer::joinSubsetIndices( joinLayerFields, subsetNames );
1160  joinedAttributeIndices = qgis::setToList( qgis::listToSet( attributes ).intersect( qgis::listToSet( subsetIndices.toList() ) ) );
1161  }
1162  else
1163  {
1164  joinedAttributeIndices = attributes;
1165  }
1166 
1167  // we don't need the join field, it is already present in the other table
1168  joinedAttributeIndices.removeAll( joinField );
1169 
1170  // select (no geometry)
1171  QgsFeatureRequest request;
1173  request.setSubsetOfAttributes( joinedAttributeIndices );
1174  request.setFilterExpression( subsetString );
1175  request.setLimit( 1 );
1176  QgsFeatureIterator fi = joinSource->getFeatures( request );
1177 
1178  // get first feature
1179  const QList<int> sourceAttrIndexes = attributesSourceToDestLayerMap.keys();
1180  QgsFeature fet;
1181  if ( fi.nextFeature( fet ) )
1182  {
1183  const QgsAttributes attr = fet.attributes();
1184 
1185  for ( const int sourceAttrIndex : sourceAttrIndexes )
1186  {
1187  if ( sourceAttrIndex == joinField )
1188  continue;
1189 
1190  const int destAttrIndex = attributesSourceToDestLayerMap.value( sourceAttrIndex );
1191 
1192  f.setAttribute( destAttrIndex, attr.at( sourceAttrIndex ) );
1193  }
1194  }
1195  else
1196  {
1197  // no suitable join feature found, keeping empty (null) attributes
1198  }
1199 }
1200 
1201 
1202 
1203 
1205 {
1206  const QgsFeatureId featureId = mRequest.filterFid();
1207 
1208  // deleted already?
1209  if ( mSource->mDeletedFeatureIds.contains( featureId ) )
1210  return false;
1211 
1212  // has changed geometry?
1213  if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) )
1214  {
1215  useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f );
1216  return true;
1217  }
1218 
1219  // added features
1220  for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter )
1221  {
1222  if ( iter->id() == featureId )
1223  {
1224  useAddedFeature( *iter, f );
1225  return true;
1226  }
1227  }
1228 
1229  // regular features
1231  if ( fi.nextFeature( f ) )
1232  {
1233  f.setFields( mSource->mFields );
1234 
1235  if ( mSource->mHasEditBuffer )
1237 
1238  if ( mHasVirtualAttributes )
1239  addVirtualAttributes( f );
1240 
1241  return true;
1242  }
1243 
1244  return false;
1245 }
1246 
1248 {
1249  QgsAttributes attrs = f.attributes();
1250 
1251  // remove all attributes that will disappear - from higher indices to lower
1252  for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
1253  {
1254  attrs.remove( mSource->mDeletedAttributeIds[idx] );
1255  }
1256 
1257  // adjust size to accommodate added attributes
1258  attrs.resize( attrs.count() + mSource->mAddedAttributes.count() );
1259 
1260  // update changed attributes
1261  if ( mSource->mChangedAttributeValues.contains( f.id() ) )
1262  {
1264  for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
1265  attrs[it.key()] = it.value();
1266  }
1267  f.setAttributes( attrs );
1268 }
1269 
1271 {
1272  if ( mSource->mChangedGeometries.contains( f.id() ) )
1274 }
1275 
1276 void QgsVectorLayerFeatureIterator::createExpressionContext()
1277 {
1278  mExpressionContext = std::make_unique< QgsExpressionContext >();
1279  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
1280  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );
1281  mExpressionContext->appendScope( new QgsExpressionContextScope( mSource->mLayerScope ) );
1282  mExpressionContext->setFeedback( mRequest.feedback() );
1283 }
1284 
1285 bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys )
1286 {
1287  Q_UNUSED( orderBys )
1288  return mDelegatedOrderByToProvider;
1289 }
1290 
1291 
1292 //
1293 // QgsVectorLayerSelectedFeatureSource
1294 //
1295 
1297  : mSource( layer )
1298  , mSelectedFeatureIds( layer->selectedFeatureIds() )
1299  , mWkbType( layer->wkbType() )
1300  , mName( layer->name() )
1301  , mLayer( layer )
1302 {}
1303 
1305 {
1306  QgsFeatureRequest req( request );
1307 
1308  // while QgsVectorLayerSelectedFeatureIterator will reject any features not in mSelectedFeatureIds,
1309  // we still tweak the feature request to only request selected feature ids wherever we can -- this
1310  // allows providers to optimise the request and avoid requesting features we don't need
1311  // note that we can't do this for some request types - e.g. expression based requests, so
1312  // in that case we just pass the request on to the provider and let QgsVectorLayerSelectedFeatureIterator
1313  // do ALL the filtering
1314  if ( req.filterFids().isEmpty() && req.filterType() == QgsFeatureRequest::FilterNone )
1315  {
1316  req.setFilterFids( mSelectedFeatureIds );
1317  }
1318  else if ( !req.filterFids().isEmpty() )
1319  {
1320  QgsFeatureIds reqIds = mSelectedFeatureIds;
1321  reqIds.intersect( req.filterFids() );
1322  req.setFilterFids( reqIds );
1323  }
1324 
1325  return QgsFeatureIterator( new QgsVectorLayerSelectedFeatureIterator( mSelectedFeatureIds, req, mSource ) );
1326 }
1327 
1329 {
1330  return mSource.crs();
1331 }
1332 
1334 {
1335  return mSource.fields();
1336 }
1337 
1339 {
1340  return mWkbType;
1341 }
1342 
1344 {
1345  return mSelectedFeatureIds.count();
1346 }
1347 
1349 {
1350  return mName;
1351 }
1352 
1354 {
1355  if ( mLayer )
1356  return mLayer->createExpressionContextScope();
1357  else
1358  return nullptr;
1359 }
1360 
1362 {
1363  if ( mLayer )
1364  return mLayer->hasSpatialIndex();
1365  else
1367 }
1368 
1369 //
1370 // QgsVectorLayerSelectedFeatureIterator
1371 //
1372 
1374 QgsVectorLayerSelectedFeatureIterator::QgsVectorLayerSelectedFeatureIterator( const QgsFeatureIds &selectedFeatureIds, const QgsFeatureRequest &request, QgsVectorLayerFeatureSource &source )
1375  : QgsAbstractFeatureIterator( request )
1376  , mSelectedFeatureIds( selectedFeatureIds )
1377 {
1378  QgsFeatureRequest sourceRequest = request;
1379  if ( sourceRequest.filterType() == QgsFeatureRequest::FilterExpression && sourceRequest.limit() > 0 )
1380  {
1381  // we can't pass the request limit to the provider here - otherwise the provider will
1382  // limit the number of returned features and may only return a bunch of matching features
1383  // which AREN'T in the selected feature set
1384  sourceRequest.setLimit( -1 );
1385  }
1386  mIterator = source.getFeatures( sourceRequest );
1387 }
1388 
1389 bool QgsVectorLayerSelectedFeatureIterator::rewind()
1390 {
1391  return mIterator.rewind();
1392 }
1393 
1394 bool QgsVectorLayerSelectedFeatureIterator::close()
1395 {
1396  return mIterator.close();
1397 }
1398 
1399 bool QgsVectorLayerSelectedFeatureIterator::fetchFeature( QgsFeature &f )
1400 {
1401  while ( mIterator.nextFeature( f ) )
1402  {
1403  if ( mSelectedFeatureIds.contains( f.id() ) )
1404  return true;
1405  }
1406  return false;
1407 }
1408 
@ DistanceWithin
Filter by distance to reference geometry.
@ BoundingBox
Filter using a bounding box.
@ NoFilter
No spatial filtering of features.
Helper template that cares of two things: 1.
void iteratorClosed()
to be called by from subclass in close()
Internal feature iterator to be implemented within data providers.
@ Success
Request was successfully updated to the source CRS, or no changes were required.
@ DistanceWithinMustBeCheckedManually
The distance within request cannot be losslessly updated to the source CRS, and callers will need to ...
void geometryToDestinationCrs(QgsFeature &feature, const QgsCoordinateTransform &transform) const
Transforms feature's geometry according to the specified coordinate transform.
QgsFeatureRequest mRequest
A copy of the feature request.
RequestToSourceCrsResult updateRequestToSourceCrs(QgsFeatureRequest &request, const QgsCoordinateTransform &transform) const SIP_THROW(QgsCsException)
Update a QgsFeatureRequest so that spatial filters are transformed to the source's coordinate referen...
bool mClosed
Sets to true, as soon as the iterator is closed.
A vector of attributes.
Definition: qgsattributes.h:58
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
Buffers information about expression fields for a vector layer.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
QSet< int > referencedAttributeIndexes(const QgsFields &fields) const
Returns a list of field name indexes obtained from the provided fields.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
bool isClosed() const
find out whether the iterator is still valid or closed already
void setInterruptionChecker(QgsFeedback *interruptionChecker)
Attach an object that can be queried regularly by the iterator to check if it must stopped.
bool isValid() const
Will return if this iterator is valid.
Represents a list of OrderByClauses, with the most important first and the least important last.
QSet< int > CORE_EXPORT usedAttributeIndices(const QgsFields &fields) const
Returns a set of used, validated attribute indices.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
std::function< void(const QgsFeature &) > invalidGeometryCallback() const
Returns the callback function to use when encountering an invalid geometry and invalidGeometryCheck()...
QgsRectangle filterRect() const
Returns the rectangle from which features will be taken.
@ GeometryNoCheck
No invalid geometry checking.
@ GeometryAbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
@ GeometrySkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
InvalidGeometryCheck invalidGeometryCheck() const
Returns the invalid geometry checking behavior.
QgsExpression * filterExpression() const
Returns the filter expression (if set).
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the iterator to check if it should be ca...
long long limit() const
Returns the maximum number of features to request, or -1 if no limit set.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
OrderBy orderBy() const
Returns a list of order by clauses specified for this feature request.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsGeometry referenceGeometry() const
Returns the reference geometry used for spatial filtering of features.
Flags flags() const
Returns the flags which affect how features are fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
QgsExpressionContext * expressionContext()
Returns the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for feature's geometries, or an invalid QgsCoordi...
@ SubsetOfAttributes
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag)
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
bool acceptFeature(const QgsFeature &feature)
Check if a feature is accepted by this requests filter.
QgsCoordinateTransformContext transformContext() const
Returns the transform context, for use when a destinationCrs() has been set and reprojection is requi...
std::shared_ptr< QgsGeometryEngine > referenceGeometryEngine() const
Returns the reference geometry engine used for spatial filtering of features.
Qgis::SpatialFilterType spatialFilterType() const
Returns the spatial filter type which is currently set on this request.
double distanceWithin() const
Returns the maximum distance from the referenceGeometry() of fetched features, if spatialFilterType()...
QgsAttributeList subsetOfAttributes() const
Returns the subset of attributes which at least need to be fetched.
QgsFeatureRequest & disableFilter()
Disables any attribute/ID filtering.
QgsFeatureRequest & setOrderBy(const OrderBy &orderBy)
Set a list of order by clauses.
FilterType filterType() const
Returns the attribute/ID filter type which is currently set on this request.
const QgsFeatureIds & filterFids() const
Returns the feature IDs that should be fetched.
@ FilterFid
Filter using feature ID.
@ FilterNone
No filter is applied.
@ FilterExpression
Filter using expression.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsFeatureId filterFid() const
Returns the feature ID that should be fetched.
SpatialIndexPresence
Enumeration of spatial index presence states.
@ SpatialIndexUnknown
Spatial index presence cannot be determined, index may or may not exist.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
Definition: qgsfeature.cpp:255
QgsAttributes attributes
Definition: qgsfeature.h:65
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:153
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:188
void setId(QgsFeatureId id)
Sets the feature id for this feature.
Definition: qgsfeature.cpp:115
QgsGeometry geometry
Definition: qgsfeature.h:67
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:214
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:223
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:320
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:163
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
QString name
Definition: qgsfield.h:60
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition: qgsfield.cpp:402
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
Definition: qgsfields.cpp:376
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
@ OriginExpression
Field is calculated from an expression.
Definition: qgsfields.h:54
@ OriginEdit
Field has been temporarily added in editing mode (originIndex = index in the list of added attributes...
Definition: qgsfields.h:53
@ OriginUnknown
It has not been specified where the field comes from.
Definition: qgsfields.h:50
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:52
@ OriginProvider
Field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
Definition: qgsfields.h:51
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
Definition: qgsfields.cpp:189
bool exists(int i) const
Returns if a field index is valid.
Definition: qgsfields.cpp:153
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int fieldOriginIndex(int fieldIdx) const
Returns the field's origin index (its meaning is specific to each type of origin).
Definition: qgsfields.cpp:197
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:470
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:107
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
This class contains information about how to simplify geometries fetched from a QgsFeatureIterator.
This is the base class for vector data providers.
virtual QgsAbstractFeatureSource * featureSource() const =0
Returns feature source object that can be used for querying provider's data.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
QgsFeatureIds deletedFeatureIds() const
Returns a list of deleted feature IDs which are not committed.
QgsChangedAttributesMap changedAttributeValues() const
Returns a map of features with changed attributes values which are not committed.
QList< QgsField > addedAttributes() const
Returns a list of added attributes fields which are not committed.
QgsFeatureMap addedFeatures() const
Returns a map of new features which are not committed.
QgsGeometryMap changedGeometries() const
Returns a map of features with changed geometries which are not committed.
QgsAttributeList deletedAttributeIds() const
Returns a list of deleted attributes fields which are not committed.
void updateChangedAttributes(QgsFeature &f)
Update feature with uncommitted attribute updates.
void useAddedFeature(const QgsFeature &src, QgsFeature &f)
QMap< const QgsVectorLayerJoinInfo *, QgsVectorLayerFeatureIterator::FetchJoinInfo > mFetchJoinInfo
Information about joins used in the current select() statement.
QgsFeatureMap::ConstIterator mFetchAddedFeaturesIt
void setInterruptionChecker(QgsFeedback *interruptionChecker) override
Attach an object that can be queried regularly by the iterator to check if it must stopped.
void addVirtualAttributes(QgsFeature &f)
Adds attributes that don't source from the provider but are added inside QGIS Includes.
bool close() override
end of iterating: free the resources / lock
void updateFeatureGeometry(QgsFeature &f)
Update feature with uncommitted geometry updates.
void addExpressionAttribute(QgsFeature &f, int attrIndex)
Adds an expression based attribute to a feature.
QMap< int, QgsExpression * > mExpressionFieldInfo
QgsGeometryMap::ConstIterator mFetchChangedGeomIt
bool prepareSimplification(const QgsSimplifyMethod &simplifyMethod) override
Setup the simplification of geometries to fetch using the specified simplify method.
void useChangedAttributeFeature(QgsFeatureId fid, const QgsGeometry &geom, QgsFeature &f)
std::shared_ptr< QgsGeometryEngine > mDistanceWithinEngine
bool fetchFeature(QgsFeature &feature) override
fetch next feature, return true on success
bool isValid() const override
Returns if this iterator is valid.
bool rewind() override
reset the iterator to the starting position
QgsVectorLayerFeatureIterator(QgsVectorLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request)
Partial snapshot of vector layer's state (only the members necessary for access to features)
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system for features retrieved from this source.
QgsChangedAttributesMap mChangedAttributeValues
std::unique_ptr< QgsAbstractFeatureSource > mProviderFeatureSource
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) override
Gets an iterator for features matching the specified request.
QgsVectorLayerFeatureSource(const QgsVectorLayer *layer)
Constructor for QgsVectorLayerFeatureSource.
~QgsVectorLayerFeatureSource() override
QString id() const
Returns the layer id of the source layer.
QgsFields fields() const
Returns the fields that will be available for features that are retrieved from this source.
std::unique_ptr< QgsVectorLayerJoinBuffer > mJoinBuffer
std::unique_ptr< QgsExpressionFieldBuffer > mExpressionFieldBuffer
bool containsJoins() const
Quick way to test if there is any join at all.
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer.
void createJoinCaches()
Calls cacheJoinLayer() for all vector joins.
static QVector< int > joinSubsetIndices(QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset)
Returns a vector of indices for use in join based on field names from the layer.
Defines left outer join from our vector layer to some other vector layer.
QString joinFieldName() const
Returns name of the field of joined layer that will be used for join.
QString targetFieldName() const
Returns name of the field of our layer that will be used for join.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet)
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer.
QHash< QString, QgsAttributes > cachedAttributes
Cache for joined attributes to provide fast lookup (size is 0 if no memory caching)
long long featureCount() const override
Returns the number of features contained in the source, or -1 if the feature count is unknown.
SpatialIndexPresence hasSpatialIndex() const override
Returns an enum value representing the presence of a valid spatial index on the source,...
QgsWkbTypes::Type wkbType() const override
Returns the geometry type for features returned by this source.
QgsFields fields() const override
Returns the fields associated with features in the source.
QgsCoordinateReferenceSystem sourceCrs() const override
Returns the coordinate reference system for features in the source.
QgsVectorLayerSelectedFeatureSource(QgsVectorLayer *layer)
Constructor for QgsVectorLayerSelectedFeatureSource, for selected features from the specified layer.
QgsExpressionContextScope * createExpressionContextScope() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QString sourceName() const override
Returns a friendly display name for the source.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Returns an iterator for the features in the source.
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Q_INVOKABLE QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
Definition: qgsfeature.h:877
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
Definition: qgsfeature.h:868
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
QMap< QgsFeatureId, QgsFeature > QgsFeatureMap
const QgsAttributeList & attributeIndexes
Join information prepared for fast attribute id mapping in QgsVectorLayerJoinBuffer::updateFeatureAtt...
void addJoinedAttributesDirect(QgsFeature &f, const QVariant &joinValue) const
std::shared_ptr< QgsVectorLayerFeatureSource > joinSource
Feature source for join.
int targetField
Index of field (of this layer) that drives the join.
const QgsVectorLayerJoinInfo * joinInfo
Canonical source of information about the join.
int indexOffset
At what position the joined fields start.
void addJoinedAttributesCached(QgsFeature &f, const QVariant &joinValue) const