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