QGIS API Documentation  3.9.0-Master (224899f119)
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 
32 {
33  QMutexLocker locker( &layer->mFeatureSourceConstructorMutex );
35  mFields = layer->fields();
36  mId = layer->id();
37 
38  // update layer's join caches if necessary
39  if ( layer->mJoinBuffer->containsJoins() )
40  layer->mJoinBuffer->createJoinCaches();
41 
42  mJoinBuffer = layer->mJoinBuffer->clone();
43 
44  mExpressionFieldBuffer = new QgsExpressionFieldBuffer( *layer->mExpressionFieldBuffer );
45  mCrs = layer->crs();
46 
47  mHasEditBuffer = layer->editBuffer();
48  if ( mHasEditBuffer )
49  {
50 #if 0
51  // TODO[MD]: after merge
52  if ( request.filterType() == QgsFeatureRequest::FilterFid )
53  {
54 
55  // only copy relevant parts
56  if ( L->editBuffer()->addedFeatures().contains( request.filterFid() ) )
57  mAddedFeatures.insert( request.filterFid(), L->editBuffer()->addedFeatures()[ request.filterFid()] );
58 
59  if ( L->editBuffer()->changedGeometries().contains( request.filterFid() ) )
60  mChangedGeometries.insert( request.filterFid(), L->editBuffer()->changedGeometries()[ request.filterFid()] );
61 
62  if ( L->editBuffer()->deletedFeatureIds().contains( request.filterFid() ) )
63  mDeletedFeatureIds.insert( request.filterFid() );
64 
65  if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
66  mChangedAttributeValues.insert( request.filterFid(), L->editBuffer()->changedAttributeValues()[ request.filterFid()] );
67 
68  if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
69  mChangedFeaturesRequest.setFilterFids( QgsFeatureIds() << request.filterFid() );
70  }
71  else
72  {
73 #endif
78  mAddedAttributes = QList<QgsField>( layer->editBuffer()->addedAttributes() );
80 #if 0
81  }
82 #endif
83  }
84 
85  std::unique_ptr< QgsExpressionContextScope > layerScope( QgsExpressionContextUtils::layerScope( layer ) );
86  mLayerScope = *layerScope;
87 }
88 
90 {
91  delete mJoinBuffer;
94 }
95 
97 {
98  // return feature iterator that does not own this source
99  return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, false, request ) );
100 }
101 
103 {
104  return mFields;
105 }
106 
108 {
109  return mCrs;
110 }
111 
113 {
114  return mId;
115 }
116 
117 
120  , mFetchedFid( false )
121 
122 {
124  {
126  }
127  try
128  {
130  }
131  catch ( QgsCsException & )
132  {
133  // can't reproject mFilterRect
134  close();
135  return;
136  }
137  if ( !mFilterRect.isNull() )
138  {
139  // update request to be the unprojected filter rect
141  }
142 
144  {
147 
149  {
150  //ensure that all fields required for filter expressions are prepared
152  attributeIndexes += mRequest.subsetOfAttributes().toSet();
153  mRequest.setSubsetOfAttributes( attributeIndexes.toList() );
154  }
155  }
156 
157  prepareFields();
158 
159  mHasVirtualAttributes = !mFetchJoinInfo.isEmpty() || !mExpressionFieldInfo.isEmpty();
160 
161  // by default provider's request is the same
163  // but we remove any destination CRS parameter - that is handled in QgsVectorLayerFeatureIterator,
164  // not at the provider level. Otherwise virtual fields depending on geometry would have incorrect
165  // values
166  if ( mRequest.destinationCrs().isValid() )
167  {
169  }
170 
172  {
173  // prepare list of attributes to match provider fields
174  QSet<int> providerSubset;
176  int nPendingFields = mSource->mFields.count();
177  const auto constSubset = subset;
178  for ( int attrIndex : constSubset )
179  {
180  if ( attrIndex < 0 || attrIndex >= nPendingFields )
181  continue;
182  if ( mSource->mFields.fieldOrigin( attrIndex ) == QgsFields::OriginProvider )
183  providerSubset << mSource->mFields.fieldOriginIndex( attrIndex );
184  }
185 
186  // This is done in order to be prepared to do fallback order bys
187  // and be sure we have the required columns.
188  // TODO:
189  // It would be nicer to first check if we can compile the order by
190  // and only modify the subset if we cannot.
191  if ( !mProviderRequest.orderBy().isEmpty() )
192  {
193  const auto usedAttributeIndices = mProviderRequest.orderBy().usedAttributeIndices( mSource->mFields );
194  for ( int attrIndex : usedAttributeIndices )
195  {
196  providerSubset << attrIndex;
197  }
198  }
199 
200  mProviderRequest.setSubsetOfAttributes( providerSubset.toList() );
201  }
202 
204  {
205  const bool needsGeom = mProviderRequest.filterExpression()->needsGeometry();
206  const auto constReferencedColumns = mProviderRequest.filterExpression()->referencedColumns();
207  for ( const QString &field : constReferencedColumns )
208  {
209  int idx = source->mFields.lookupField( field );
210 
211  // If there are fields in the expression which are not of origin provider, the provider will not be able to filter based on them.
212  // In this case we disable the expression filter.
213  if ( source->mFields.fieldOrigin( idx ) != QgsFields::OriginProvider )
214  {
216  // can't limit at provider side
218  if ( needsGeom )
219  {
220  // have to get geometry from provider in order to evaluate expression on client
222  }
223  break;
224  }
225  }
226  }
227 
228  if ( mSource->mHasEditBuffer )
229  {
231  QgsFeatureIds changedIds;
232  QgsChangedAttributesMap::const_iterator attIt = mSource->mChangedAttributeValues.constBegin();
233  for ( ; attIt != mSource->mChangedAttributeValues.constEnd(); ++attIt )
234  {
235  changedIds << attIt.key();
236  }
238 
239  if ( mChangedFeaturesRequest.limit() > 0 )
240  {
241  int providerLimit = mProviderRequest.limit();
242 
243  // features may be deleted in buffer, so increase limit sent to provider
244  providerLimit += mSource->mDeletedFeatureIds.size();
245 
247  {
248  // attribute changes may mean some features no longer match expression, so increase limit sent to provider
249  providerLimit += mSource->mChangedAttributeValues.size();
250  }
251 
253  {
254  // geometry changes may mean some features no longer match expression or rect, so increase limit sent to provider
255  providerLimit += mSource->mChangedGeometries.size();
256  }
257 
258  mProviderRequest.setLimit( providerLimit );
259  mChangedFeaturesRequest.setLimit( providerLimit );
260  }
261  }
262 
263  if ( request.filterType() == QgsFeatureRequest::FilterFid )
264  {
265  mFetchedFid = false;
266  }
267  else // no filter or filter by rect
268  {
269  if ( mSource->mHasEditBuffer )
270  {
272  }
273  else
274  {
276  }
277 
279  }
280 }
281 
282 
284 {
285  qDeleteAll( mExpressionFieldInfo );
286 
287  close();
288 }
289 
290 
291 
293 {
294  f.setValid( false );
295 
296  if ( mClosed )
297  return false;
298 
300  {
301  if ( mFetchedFid )
302  return false;
303  bool res = nextFeatureFid( f );
304  if ( res && postProcessFeature( f ) )
305  {
306  mFetchedFid = true;
307  return res;
308  }
309  else
310  {
311  return false;
312  }
313  }
314 
315  if ( !mFilterRect.isNull() )
316  {
317  if ( fetchNextChangedGeomFeature( f ) )
318  return true;
319 
320  // no more changed geometries
321  }
322 
324  {
326  return true;
327 
328  if ( fetchNextChangedGeomFeature( f ) )
329  return true;
330 
331  // no more changed features
332  }
333 
334  while ( fetchNextAddedFeature( f ) )
335  {
336  return true;
337  }
338  // no more added features
339 
340  if ( mProviderIterator.isClosed() )
341  {
344  mProviderIterator.setInterruptionChecker( mInterruptionChecker );
345  }
346 
347  while ( mProviderIterator.nextFeature( f ) )
348  {
349  if ( mFetchConsidered.contains( f.id() ) )
350  continue;
351 
352  // TODO[MD]: just one resize of attributes
353  f.setFields( mSource->mFields );
354 
355  // update attributes
356  if ( mSource->mHasEditBuffer )
358 
359  if ( mHasVirtualAttributes )
361 
363  {
364  //filtering by expression, and couldn't do it on the provider side
367  {
368  //feature did not match filter
369  continue;
370  }
371  }
372 
373  // update geometry
374  // TODO[MK]: FilterRect check after updating the geometry
377 
378  if ( !postProcessFeature( f ) )
379  continue;
380 
381  return true;
382  }
383  // no more provider features
384 
385  close();
386  return false;
387 }
388 
389 
390 
392 {
393  if ( mClosed )
394  return false;
395 
397  {
398  mFetchedFid = false;
399  }
400  else
401  {
404  }
405 
406  return true;
407 }
408 
410 {
411  if ( mClosed )
412  return false;
413 
415 
416  iteratorClosed();
417 
418  mClosed = true;
419  return true;
420 }
421 
423 {
424  mProviderIterator.setInterruptionChecker( interruptionChecker );
425  mInterruptionChecker = interruptionChecker;
426 }
427 
429 {
430  return mProviderIterator.isValid();
431 }
432 
434 {
435  while ( mFetchAddedFeaturesIt-- != mSource->mAddedFeatures.constBegin() )
436  {
438 
439  if ( mFetchConsidered.contains( fid ) )
440  // must have changed geometry outside rectangle
441  continue;
442 
444 
445  // can't test for feature acceptance until after calling useAddedFeature
446  // since acceptFeature may rely on virtual fields
447  if ( !mRequest.acceptFeature( f ) )
448  // skip features which are not accepted by the filter
449  continue;
450 
451  if ( !postProcessFeature( f ) )
452  continue;
453 
454  return true;
455  }
456 
458  return false; // no more added features
459 }
460 
461 
463 {
464  // since QgsFeature is implicitly shared, it's more efficient to just copy the
465  // whole feature, even if flags like NoGeometry or a subset of attributes is set at the request.
466  // This helps potentially avoid an unnecessary detach of the feature
467  f = src;
468  f.setValid( true );
469  f.setFields( mSource->mFields );
470 
471  if ( mHasVirtualAttributes )
473 }
474 
475 
476 
478 {
479  // check if changed geometries are in rectangle
481  {
482  QgsFeatureId fid = mFetchChangedGeomIt.key();
483 
484  if ( mFetchConsidered.contains( fid ) )
485  // skip deleted features
486  continue;
487 
488  mFetchConsidered << fid;
489 
490  if ( !mFilterRect.isNull() && !mFetchChangedGeomIt->intersects( mFilterRect ) )
491  // skip changed geometries not in rectangle and don't check again
492  continue;
493 
495 
497  {
500  {
501  continue;
502  }
503  }
504 
505  if ( postProcessFeature( f ) )
506  {
507  // return complete feature
509  return true;
510  }
511  }
512 
513  return false; // no more changed geometries
514 }
515 
517 {
519  {
520  if ( mFetchConsidered.contains( f.id() ) )
521  // skip deleted features and those already handled by the geometry
522  continue;
523 
524  mFetchConsidered << f.id();
525 
527 
528  if ( mHasVirtualAttributes )
530 
532  if ( mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() && postProcessFeature( f ) )
533  {
534  return true;
535  }
536  }
537 
538  return false;
539 }
540 
541 
543 {
544  f.setId( fid );
545  f.setValid( true );
546  f.setFields( mSource->mFields );
547 
550  {
551  f.setGeometry( geom );
552  }
553 
554  bool subsetAttrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes );
555  if ( !subsetAttrs || !mRequest.subsetOfAttributes().isEmpty() )
556  {
557  // retrieve attributes from provider
558  QgsFeature tmp;
559  //mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes );
560  QgsFeatureRequest request;
562  if ( subsetAttrs )
563  {
565  }
567  if ( fi.nextFeature( tmp ) )
568  {
571  f.setAttributes( tmp.attributes() );
572  }
573  }
574 
576 }
577 
578 
579 
581 {
583 
586 }
587 
589 {
590  if ( !mSource->mFields.exists( fieldIdx ) )
591  return;
592 
593  if ( mSource->mFields.fieldOrigin( fieldIdx ) != QgsFields::OriginJoin )
594  return;
595 
596  int sourceLayerIndex;
597  const QgsVectorLayerJoinInfo *joinInfo = mSource->mJoinBuffer->joinForFieldIndex( fieldIdx, mSource->mFields, sourceLayerIndex );
598  Q_ASSERT( joinInfo );
599 
600  QgsVectorLayer *joinLayer = joinInfo->joinLayer();
601  if ( !joinLayer )
602  return; // invalid join (unresolved reference to layer)
603 
604  if ( !mFetchJoinInfo.contains( joinInfo ) )
605  {
606  FetchJoinInfo info;
607  info.joinInfo = joinInfo;
608  info.joinLayer = joinLayer;
611  info.joinField = joinLayer->fields().indexFromName( joinInfo->joinFieldName() );
612 
613  // for joined fields, we always need to request the targetField from the provider too
614  if ( !mPreparedFields.contains( info.targetField ) && !mFieldsToPrepare.contains( info.targetField ) )
615  mFieldsToPrepare << info.targetField;
616 
619 
620  mFetchJoinInfo.insert( joinInfo, info );
621  }
622 
623  // store field source index - we'll need it when fetching from provider
624  mFetchJoinInfo[ joinInfo ].attributes.push_back( sourceLayerIndex );
625 }
626 
628 {
629  const QList<QgsExpressionFieldBuffer::ExpressionField> &exps = mSource->mExpressionFieldBuffer->expressions();
630 
631  int oi = mSource->mFields.fieldOriginIndex( fieldIdx );
632  std::unique_ptr<QgsExpression> exp = qgis::make_unique<QgsExpression>( exps[oi].cachedExpression );
633 
634  QgsDistanceArea da;
636  da.setEllipsoid( QgsProject::instance()->ellipsoid() );
637  exp->setGeomCalculator( &da );
638  exp->setDistanceUnits( QgsProject::instance()->distanceUnits() );
639  exp->setAreaUnits( QgsProject::instance()->areaUnits() );
640 
641  if ( !mExpressionContext )
642  createExpressionContext();
643  exp->prepare( mExpressionContext.get() );
644  const auto referencedColumns = exp->referencedColumns();
645  for ( const QString &col : referencedColumns )
646  {
647  if ( mSource->fields().lookupField( col ) == fieldIdx )
648  {
649  // circular reference - expression depends on column itself
650  return;
651  }
652  }
653 
654  for ( const QString &col : referencedColumns )
655  {
656  int dependentFieldIdx = mSource->mFields.lookupField( col );
658  {
659  mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() << dependentFieldIdx );
660  }
661  // also need to fetch this dependent field
662  if ( !mPreparedFields.contains( dependentFieldIdx ) && !mFieldsToPrepare.contains( dependentFieldIdx ) )
663  mFieldsToPrepare << dependentFieldIdx;
664  }
665 
666  if ( exp->needsGeometry() )
667  {
668  mRequest.setFlags( mRequest.flags() & ~QgsFeatureRequest::NoGeometry );
669  }
670 
671  mExpressionFieldInfo.insert( fieldIdx, exp.release() );
672 }
673 
675 {
676  mPreparedFields.clear();
677  mFieldsToPrepare.clear();
678  mFetchJoinInfo.clear();
679  mOrderedJoinInfoList.clear();
680 
681  mExpressionContext.reset();
682 
684 
685  while ( !mFieldsToPrepare.isEmpty() )
686  {
687  int fieldIdx = mFieldsToPrepare.takeFirst();
688  if ( mPreparedFields.contains( fieldIdx ) )
689  continue;
690 
691  mPreparedFields << fieldIdx;
692  prepareField( fieldIdx );
693  }
694 
695  //sort joins by dependency
696  if ( !mFetchJoinInfo.empty() )
697  {
698  createOrderedJoinList();
699  }
700 }
701 
702 void QgsVectorLayerFeatureIterator::createOrderedJoinList()
703 {
704  mOrderedJoinInfoList = mFetchJoinInfo.values();
705  if ( mOrderedJoinInfoList.size() < 2 )
706  {
707  return;
708  }
709 
710  QSet<int> resolvedFields; //todo: get provider / virtual fields without joins
711 
712  //add all provider fields without joins as resolved fields
713  QList< int >::const_iterator prepFieldIt = mPreparedFields.constBegin();
714  for ( ; prepFieldIt != mPreparedFields.constEnd(); ++prepFieldIt )
715  {
716  if ( mSource->mFields.fieldOrigin( *prepFieldIt ) != QgsFields::OriginJoin )
717  {
718  resolvedFields.insert( *prepFieldIt );
719  }
720  }
721 
722  //iterate through the joins. If target field is not yet covered, move the entry to the end of the list
723 
724  //some join combinations might not have a resolution at all
725  int maxIterations = ( mOrderedJoinInfoList.size() + 1 ) * mOrderedJoinInfoList.size() / 2.0;
726  int currentIteration = 0;
727 
728  for ( int i = 0; i < mOrderedJoinInfoList.size() - 1; ++i )
729  {
730  if ( !resolvedFields.contains( mOrderedJoinInfoList.at( i ).targetField ) )
731  {
732  mOrderedJoinInfoList.append( mOrderedJoinInfoList.at( i ) );
733  mOrderedJoinInfoList.removeAt( i );
734  --i;
735  }
736  else
737  {
738  int offset = mOrderedJoinInfoList.at( i ).indexOffset;
739  int joinField = mOrderedJoinInfoList.at( i ).joinField;
740 
741  QgsAttributeList attributes = mOrderedJoinInfoList.at( i ).attributes;
742  for ( int n = 0; n < attributes.size(); n++ )
743  {
744  if ( n != joinField )
745  {
746  resolvedFields.insert( joinField < n ? n + offset - 1 : n + offset );
747  }
748  }
749  }
750 
751  ++currentIteration;
752  if ( currentIteration >= maxIterations )
753  {
754  break;
755  }
756  }
757 }
758 
759 bool QgsVectorLayerFeatureIterator::postProcessFeature( QgsFeature &feature )
760 {
761  bool result = checkGeometryValidity( feature );
762  if ( result )
764  return result;
765 }
766 
767 bool QgsVectorLayerFeatureIterator::checkGeometryValidity( const QgsFeature &feature )
768 {
769  if ( !feature.hasGeometry() )
770  return true;
771 
772  switch ( mRequest.invalidGeometryCheck() )
773  {
775  return true;
776 
778  {
779  if ( !feature.geometry().isGeosValid() )
780  {
781  QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::Critical );
783  {
784  mRequest.invalidGeometryCallback()( feature );
785  }
786  return false;
787  }
788  break;
789  }
790 
792  if ( !feature.geometry().isGeosValid() )
793  {
794  QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::Critical );
795  close();
797  {
798  mRequest.invalidGeometryCallback()( feature );
799  }
800  return false;
801  }
802  break;
803  }
804 
805  return true;
806 }
807 
809 {
810  switch ( mSource->mFields.fieldOrigin( fieldIdx ) )
811  {
813  prepareExpression( fieldIdx );
814  break;
815 
818  {
819  prepareJoin( fieldIdx );
820  }
821  break;
822 
826  break;
827  }
828 }
829 
831 {
832  QList< FetchJoinInfo >::const_iterator joinIt = mOrderedJoinInfoList.constBegin();
833  for ( ; joinIt != mOrderedJoinInfoList.constEnd(); ++joinIt )
834  {
835  QVariant targetFieldValue = f.attribute( joinIt->targetField );
836  if ( !targetFieldValue.isValid() )
837  continue;
838 
839  const QHash< QString, QgsAttributes> &memoryCache = joinIt->joinInfo->cachedAttributes;
840  if ( memoryCache.isEmpty() )
841  joinIt->addJoinedAttributesDirect( f, targetFieldValue );
842  else
843  joinIt->addJoinedAttributesCached( f, targetFieldValue );
844  }
845 }
846 
848 {
849  // make sure we have space for newly added attributes
850  QgsAttributes attr = f.attributes();
851  attr.resize( mSource->mFields.count() ); // Provider attrs count + joined attrs count + expression attrs count
852  f.setAttributes( attr );
853 
854  // possible TODO - handle combinations of expression -> join -> expression -> join?
855  // but for now, write that off as too complex and an unlikely rare, unsupported use case
856 
857  QList< int > fetchedVirtualAttributes;
858  //first, check through joins for any virtual fields we need
859  QMap<const QgsVectorLayerJoinInfo *, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
860  for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
861  {
862  if ( mExpressionFieldInfo.contains( joinIt->targetField ) )
863  {
864  // have to calculate expression field before we can handle this join
865  addExpressionAttribute( f, joinIt->targetField );
866  fetchedVirtualAttributes << joinIt->targetField;
867  }
868  }
869 
870  if ( !mFetchJoinInfo.isEmpty() )
871  addJoinedAttributes( f );
872 
873  // add remaining expression fields
874  if ( !mExpressionFieldInfo.isEmpty() )
875  {
876  QMap<int, QgsExpression *>::ConstIterator it = mExpressionFieldInfo.constBegin();
877  for ( ; it != mExpressionFieldInfo.constEnd(); ++it )
878  {
879  if ( fetchedVirtualAttributes.contains( it.key() ) )
880  continue;
881 
882  addExpressionAttribute( f, it.key() );
883  }
884  }
885 }
886 
888 {
889  QgsExpression *exp = mExpressionFieldInfo.value( attrIndex );
890  if ( exp )
891  {
892  if ( !mExpressionContext )
893  createExpressionContext();
894 
895  mExpressionContext->setFeature( f );
896  QVariant val = exp->evaluate( mExpressionContext.get() );
897  ( void )mSource->mFields.at( attrIndex ).convertCompatible( val );
898  f.setAttribute( attrIndex, val );
899  }
900  else
901  {
902  f.setAttribute( attrIndex, QVariant() );
903  }
904 }
905 
907 {
908  Q_UNUSED( simplifyMethod )
909  return false;
910 }
911 
912 bool QgsVectorLayerFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
913 {
914  Q_UNUSED( methodType )
915  return false;
916 }
917 
918 
920 {
921  const QHash<QString, QgsAttributes> &memoryCache = joinInfo->cachedAttributes;
922  QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
923  if ( it == memoryCache.constEnd() )
924  return; // joined value not found -> leaving the attributes empty (null)
925 
926  int index = indexOffset;
927 
928  const QgsAttributes &featureAttributes = it.value();
929  for ( int i = 0; i < featureAttributes.count(); ++i )
930  {
931  f.setAttribute( index++, featureAttributes.at( i ) );
932  }
933 }
934 
935 
936 
938 {
939  // no memory cache, query the joined values by setting substring
940  QString subsetString;
941 
942  QString joinFieldName = joinInfo->joinFieldName();
943 
944  subsetString.append( QStringLiteral( "\"%1\"" ).arg( joinFieldName ) );
945 
946  if ( joinValue.isNull() )
947  {
948  subsetString += QLatin1String( " IS NULL" );
949  }
950  else
951  {
952  QString v = joinValue.toString();
953  switch ( joinValue.type() )
954  {
955  case QVariant::Int:
956  case QVariant::LongLong:
957  case QVariant::Double:
958  break;
959 
960  default:
961  case QVariant::String:
962  v.replace( '\'', QLatin1String( "''" ) );
963  v.prepend( '\'' ).append( '\'' );
964  break;
965  }
966  subsetString += '=' + v;
967  }
968 
969  // maybe user requested just a subset of layer's attributes
970  // so we do not have to cache everything
971  QVector<int> subsetIndices;
972  if ( joinInfo->hasSubset() )
973  {
974  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinInfo );
975  subsetIndices = QgsVectorLayerJoinBuffer::joinSubsetIndices( joinLayer, subsetNames );
976  }
977 
978  // select (no geometry)
979  QgsFeatureRequest request;
981  request.setSubsetOfAttributes( attributes );
982  request.setFilterExpression( subsetString );
983  request.setLimit( 1 );
984  QgsFeatureIterator fi = joinLayer->getFeatures( request );
985 
986  // get first feature
987  QgsFeature fet;
988  if ( fi.nextFeature( fet ) )
989  {
990  int index = indexOffset;
991  QgsAttributes attr = fet.attributes();
992  if ( joinInfo->hasSubset() )
993  {
994  for ( int i = 0; i < subsetIndices.count(); ++i )
995  f.setAttribute( index++, attr.at( subsetIndices.at( i ) ) );
996  }
997  else
998  {
999  // use all fields except for the one used for join (has same value as exiting field in target layer)
1000  for ( int i = 0; i < attr.count(); ++i )
1001  {
1002  if ( i == joinField )
1003  continue;
1004 
1005  f.setAttribute( index++, attr.at( i ) );
1006  }
1007  }
1008  }
1009  else
1010  {
1011  // no suitable join feature found, keeping empty (null) attributes
1012  }
1013 }
1014 
1015 
1016 
1017 
1019 {
1020  QgsFeatureId featureId = mRequest.filterFid();
1021 
1022  // deleted already?
1023  if ( mSource->mDeletedFeatureIds.contains( featureId ) )
1024  return false;
1025 
1026  // has changed geometry?
1027  if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) )
1028  {
1029  useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f );
1030  return true;
1031  }
1032 
1033  // added features
1034  for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter )
1035  {
1036  if ( iter->id() == featureId )
1037  {
1038  useAddedFeature( *iter, f );
1039  return true;
1040  }
1041  }
1042 
1043  // regular features
1045  if ( fi.nextFeature( f ) )
1046  {
1047  f.setFields( mSource->mFields );
1048 
1049  if ( mSource->mHasEditBuffer )
1051 
1052  if ( mHasVirtualAttributes )
1053  addVirtualAttributes( f );
1054 
1055  return true;
1056  }
1057 
1058  return false;
1059 }
1060 
1062 {
1063  QgsAttributes attrs = f.attributes();
1064 
1065  // remove all attributes that will disappear - from higher indices to lower
1066  for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
1067  {
1068  attrs.remove( mSource->mDeletedAttributeIds[idx] );
1069  }
1070 
1071  // adjust size to accommodate added attributes
1072  attrs.resize( attrs.count() + mSource->mAddedAttributes.count() );
1073 
1074  // update changed attributes
1075  if ( mSource->mChangedAttributeValues.contains( f.id() ) )
1076  {
1078  for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
1079  attrs[it.key()] = it.value();
1080  }
1081  f.setAttributes( attrs );
1082 }
1083 
1085 {
1086  if ( mSource->mChangedGeometries.contains( f.id() ) )
1088 }
1089 
1090 void QgsVectorLayerFeatureIterator::createExpressionContext()
1091 {
1092  mExpressionContext = qgis::make_unique< QgsExpressionContext >();
1093  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
1094  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );
1095  mExpressionContext->appendScope( new QgsExpressionContextScope( mSource->mLayerScope ) );
1096 }
1097 
1098 bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys )
1099 {
1100  Q_UNUSED( orderBys )
1101  return true;
1102 }
1103 
1104 
1105 //
1106 // QgsVectorLayerSelectedFeatureSource
1107 //
1108 
1110  : mSource( layer )
1111  , mSelectedFeatureIds( layer->selectedFeatureIds() )
1112  , mWkbType( layer->wkbType() )
1113  , mName( layer->name() )
1114  , mLayer( layer )
1115 {}
1116 
1118 {
1119  QgsFeatureRequest req( request );
1120 
1121  // while QgsVectorLayerSelectedFeatureIterator will reject any features not in mSelectedFeatureIds,
1122  // we still tweak the feature request to only request selected feature ids wherever we can -- this
1123  // allows providers to optimise the request and avoid requesting features we don't need
1124  // note that we can't do this for some request types - e.g. expression based requests, so
1125  // in that case we just pass the request on to the provider and let QgsVectorLayerSelectedFeatureIterator
1126  // do ALL the filtering
1127  if ( req.filterFids().isEmpty() && req.filterType() == QgsFeatureRequest::FilterNone )
1128  {
1129  req.setFilterFids( mSelectedFeatureIds );
1130  }
1131  else if ( !req.filterFids().isEmpty() )
1132  {
1133  QgsFeatureIds reqIds = mSelectedFeatureIds;
1134  reqIds.intersect( req.filterFids() );
1135  req.setFilterFids( reqIds );
1136  }
1137 
1138  return QgsFeatureIterator( new QgsVectorLayerSelectedFeatureIterator( mSelectedFeatureIds, req, mSource ) );
1139 }
1140 
1142 {
1143  return mSource.crs();
1144 }
1145 
1147 {
1148  return mSource.fields();
1149 }
1150 
1152 {
1153  return mWkbType;
1154 }
1155 
1157 {
1158  return mSelectedFeatureIds.count();
1159 }
1160 
1162 {
1163  return mName;
1164 }
1165 
1167 {
1168  if ( mLayer )
1169  return mLayer->createExpressionContextScope();
1170  else
1171  return nullptr;
1172 }
1173 
1174 //
1175 // QgsVectorLayerSelectedFeatureIterator
1176 //
1177 
1179 QgsVectorLayerSelectedFeatureIterator::QgsVectorLayerSelectedFeatureIterator( const QgsFeatureIds &selectedFeatureIds, const QgsFeatureRequest &request, QgsVectorLayerFeatureSource &source )
1180  : QgsAbstractFeatureIterator( request )
1181  , mSelectedFeatureIds( selectedFeatureIds )
1182 {
1183  QgsFeatureRequest sourceRequest = request;
1184  if ( sourceRequest.filterType() == QgsFeatureRequest::FilterExpression && sourceRequest.limit() > 0 )
1185  {
1186  // we can't pass the request limit to the provider here - otherwise the provider will
1187  // limit the number of returned features and may only return a bunch of matching features
1188  // which AREN'T in the selected feature set
1189  sourceRequest.setLimit( -1 );
1190  }
1191  mIterator = source.getFeatures( sourceRequest );
1192 }
1193 
1194 bool QgsVectorLayerSelectedFeatureIterator::rewind()
1195 {
1196  return mIterator.rewind();
1197 }
1198 
1199 bool QgsVectorLayerSelectedFeatureIterator::close()
1200 {
1201  return mIterator.close();
1202 }
1203 
1204 bool QgsVectorLayerSelectedFeatureIterator::fetchFeature( QgsFeature &f )
1205 {
1206  while ( mIterator.nextFeature( f ) )
1207  {
1208  if ( mSelectedFeatureIds.contains( f.id() ) )
1209  return true;
1210  }
1211  return false;
1212 }
1213 
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
QList< QgsExpressionFieldBuffer::ExpressionField > expressions() const
QgsAbstractFeatureSource * mProviderFeatureSource
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature&#39;s geometries.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Returns an iterator for the features in the source.
QgsFeatureId id
Definition: qgsfeature.h:64
void addJoinedAttributesDirect(QgsFeature &f, const QVariant &joinValue) const
Wrapper for iterator of features from vector data provider or vector layer.
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
Definition: qgsfeature.h:566
const QgsVectorLayerJoinInfo * joinInfo
Canonical source of information about the join.
QString targetFieldName() const
Returns name of the field of our layer that will be used for join.
long limit() const
Returns the maximum number of features to request, or -1 if no limit set.
bool acceptFeature(const QgsFeature &feature)
Check if a feature is accepted by this requests filter.
void geometryToDestinationCrs(QgsFeature &feature, const QgsCoordinateTransform &transform) const
Transforms feature&#39;s geometry according to the specified coordinate transform.
QgsCoordinateReferenceSystem sourceCrs() const override
Returns the coordinate reference system for features in the source.
Filter using feature ID.
QgsVectorLayerJoinBuffer * mJoinBuffer
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system for features retrieved from this source.
bool containsJoins() const
Quick way to test if there is any join at all.
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:50
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field&#39;s origin (value from an enumeration)
Definition: qgsfields.cpp:189
void setInterruptionChecker(QgsFeedback *interruptionChecker) override
Attach an object that can be queried regularly by the iterator to check if it must stopped...
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:162
QString sourceName() const override
Returns a friendly display name for the source.
const Flags & flags() const
void createJoinCaches()
Calls cacheJoinLayer() for all vector joins.
QMap< int, QgsExpression * > mExpressionFieldInfo
void addExpressionAttribute(QgsFeature &f, int attrIndex)
Adds an expression based attribute to a feature.
QgsFeatureMap::ConstIterator mFetchAddedFeaturesIt
QgsGeometryMap::ConstIterator mFetchChangedGeomIt
QgsRectangle filterRectToSourceCrs(const QgsCoordinateTransform &transform) const SIP_THROW(QgsCsException)
Returns a rectangle representing the original request&#39;s QgsFeatureRequest::filterRect().
Field has been temporarily added in editing mode (originIndex = index in the list of added attributes...
Definition: qgsfields.h:51
bool exists(int i) const
Returns if a field index is valid.
Definition: qgsfields.cpp:153
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsVectorLayerFeatureIterator(QgsVectorLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request)
QgsVectorLayerSelectedFeatureSource(QgsVectorLayer *layer)
Constructor for QgsVectorLayerSelectedFeatureSource, for selected features from the specified layer...
QgsExpressionContext * expressionContext()
Returns the expression context used to evaluate filter expressions.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) override
Gets an iterator for features matching the specified request.
QgsFeatureId filterFid() const
Gets the feature ID that should be fetched.
QVariant evaluate()
Evaluate the feature and return the result.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for feature&#39;s geometries, or an invalid QgsCoordi...
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())=0
Gets an iterator for features matching the specified request.
bool convertCompatible(QVariant &v) const
Converts the provided variant to a compatible format.
Definition: qgsfield.cpp:281
FilterType filterType() const
Returns the filter type which is currently set on this request.
Container of fields for a vector layer.
Definition: qgsfields.h:42
const QgsFeatureIds & filterFids() const
Gets feature IDs that should be fetched.
QSet< int > referencedAttributeIndexes(const QgsFields &fields) const
Returns a list of field name indexes obtained from the provided fields.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:121
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:127
bool setAttribute(int field, const QVariant &attr)
Set an attribute&#39;s value by field index.
Definition: qgsfeature.cpp:211
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
Field comes from the underlying data provider of the vector layer (originIndex = index in provider&#39;s ...
Definition: qgsfields.h:49
bool mClosed
Sets to true, as soon as the iterator is closed.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QgsChangedAttributesMap changedAttributeValues() const
Returns a map of features with changed attributes values which are not committed. ...
const QgsAttributeList & attributeIndexes
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
QgsVectorLayer * joinLayer
Resolved pointer to the joined layer.
const QgsRectangle & filterRect() const
Returns the rectangle from which features will be taken.
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsFeatureIds deletedFeatureIds() const
Returns a list of deleted feature IDs which are not committed.
It has not been specified where the field comes from.
Definition: qgsfields.h:48
QgsFields fields() const override
Returns the fields associated with features in the source.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
int joinField
Index of field (of the joined layer) must have equal value.
QgsExpression * filterExpression() const
Returns the filter expression if set.
int fieldOriginIndex(int fieldIdx) const
Gets field&#39;s origin index (its meaning is specific to each type of origin)
Definition: qgsfields.cpp:197
virtual QgsAbstractFeatureSource * featureSource() const =0
Returns feature source object that can be used for querying provider&#39;s data.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:44
void updateChangedAttributes(QgsFeature &f)
Update feature with uncommitted attribute updates.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets feature ID that should be fetched.
QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on...
QgsGeometryMap changedGeometries() const
Returns a map of features with changed geometries which are not committed.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
void iteratorClosed()
to be called by from subclass in close()
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
void useAddedFeature(const QgsFeature &src, QgsFeature &f)
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
bool isGeosValid(QgsGeometry::ValidityFlags flags=nullptr) const
Checks validity of the geometry using GEOS.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet) ...
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
Definition: qgsfields.cpp:351
QgsFields fields() const
Returns the fields that will be available for features that are retrieved from this source...
QgsFeatureRequest & disableFilter()
Disables filter conditions.
Internal feature iterator to be implemented within data providers.
void updateFeatureGeometry(QgsFeature &f)
Update feature with uncommitted geometry updates.
bool prepareSimplification(const QgsSimplifyMethod &simplifyMethod) override
Setup the simplification of geometries to fetch using the specified simplify method.
void addJoinedAttributesCached(QgsFeature &f, const QVariant &joinValue) const
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Defines left outer join from our vector layer to some other vector layer.
void setInterruptionChecker(QgsFeedback *interruptionChecker)
Attach an object that can be queried regularly by the iterator to check if it must stopped...
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
bool fetchFeature(QgsFeature &feature) override
fetch next feature, return true on success
QgsAttributeList deletedAttributeIds() const
Returns a list of deleted attributes fields which are not committed.
QgsExpressionContextScope * createExpressionContextScope() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsWkbTypes::Type wkbType() const override
Returns the geometry type for features returned by this source.
No invalid geometry checking.
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag)
void setId(QgsFeatureId id)
Sets the feature ID for this feature.
Definition: qgsfeature.cpp:112
Single scope for storing variables and functions for use within a QgsExpressionContext.
QgsExpressionFieldBuffer * mExpressionFieldBuffer
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
QgsAttributeList subsetOfAttributes() const
Returns the subset of attributes which at least need to be fetched.
Partial snapshot of vector layer&#39;s state (only the members necessary for access to features) ...
int indexOffset
At what position the joined fields start.
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:97
void useChangedAttributeFeature(QgsFeatureId fid, const QgsGeometry &geom, QgsFeature &f)
A general purpose distance and area calculator, capable of performing ellipsoid based calculations...
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:188
QMap< QgsFeatureId, QgsFeature > QgsFeatureMap
long featureCount() const override
Returns the number of features contained in the source, or -1 if the feature count is unknown...
QgsFeatureMap addedFeatures() const
Returns a map of new features which are not committed.
QgsFeatureRequest mRequest
A copy of the feature request.
int targetField
Index of field (of this layer) that drives the join.
Buffers information about expression fields for a vector layer.
No filter is applied.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
Definition: qgsfeature.h:557
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
bool rewind() override
reset the iterator to the starting position
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:441
QMap< const QgsVectorLayerJoinInfo *, QgsVectorLayerFeatureIterator::FetchJoinInfo > mFetchJoinInfo
Information about joins used in the current select() statement.
This class represents a coordinate reference system (CRS).
bool close() override
end of iterating: free the resources / lock
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.
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
InvalidGeometryCheck invalidGeometryCheck() const
Returns the invalid geometry checking behavior.
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:436
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
Class for doing transforms between two map coordinate systems.
Join information prepared for fast attribute id mapping in QgsVectorLayerJoinBuffer::updateFeatureAtt...
This class contains information about how to simplify geometries fetched from a QgsFeatureIterator.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
QgsGeometry geometry
Definition: qgsfeature.h:67
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
QString id() const
Returns the layer id of the source layer.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
QgsChangedAttributesMap mChangedAttributeValues
OrderBy orderBy() const
Returns a list of order by clauses specified for this feature request.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QList< QgsField > addedAttributes() const
Returns a list of added attributes fields which are not committed.
A vector of attributes.
Definition: qgsattributes.h:57
Represents a vector layer which manages a vector based data sets.
bool isValid() const override
Returns if this iterator is valid.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
Field is calculated from an expression.
Definition: qgsfields.h:52
QString joinFieldName() const
Returns name of the field of joined layer that will be used for join.
QgsCoordinateTransformContext transformContext() const
Returns the transform context, for use when a destinationCrs() has been set and reprojection is requi...
virtual bool isValid() const
Will return if this iterator is valid.
bool isClosed() const
find out whether the iterator is still valid or closed already
QgsVectorLayerFeatureSource(const QgsVectorLayer *layer)
Constructor for QgsVectorLayerFeatureSource.
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer.
QgsAttributes attributes
Definition: qgsfeature.h:65
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
std::function< void(const QgsFeature &) > invalidGeometryCallback() const
Returns the callback function to use when encountering an invalid geometry and invalidGeometryCheck()...
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:86
int joinedFieldsOffset(const QgsVectorLayerJoinInfo *info, const QgsFields &fields)
Find out what is the first index of the join within fields.
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer.
void addVirtualAttributes(QgsFeature &f)
Adds attributes that don&#39;t source from the provider but are added inside QGIS Includes.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
Helper template that cares of two things: 1.
QSet< int > CORE_EXPORT usedAttributeIndices(const QgsFields &fields) const
Returns a set of used, validated attribute indices.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.