QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  exp->prepare( mExpressionContext.get() );
642  const auto referencedColumns = exp->referencedColumns();
643  for ( const QString &col : referencedColumns )
644  {
645  if ( mSource->fields().lookupField( col ) == fieldIdx )
646  {
647  // circular reference - expression depends on column itself
648  return;
649  }
650  }
651 
652  for ( const QString &col : referencedColumns )
653  {
654  int dependentFieldIdx = mSource->mFields.lookupField( col );
656  {
657  mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() << dependentFieldIdx );
658  }
659  // also need to fetch this dependent field
660  if ( !mPreparedFields.contains( dependentFieldIdx ) && !mFieldsToPrepare.contains( dependentFieldIdx ) )
661  mFieldsToPrepare << dependentFieldIdx;
662  }
663 
664  if ( exp->needsGeometry() )
665  {
666  mRequest.setFlags( mRequest.flags() & ~QgsFeatureRequest::NoGeometry );
667  }
668 
669  mExpressionFieldInfo.insert( fieldIdx, exp.release() );
670 }
671 
673 {
674  mPreparedFields.clear();
675  mFieldsToPrepare.clear();
676  mFetchJoinInfo.clear();
677  mOrderedJoinInfoList.clear();
678 
679  mExpressionContext.reset( new QgsExpressionContext() );
680  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
681  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );
682  mExpressionContext->appendScope( new QgsExpressionContextScope( mSource->mLayerScope ) );
683 
685 
686  while ( !mFieldsToPrepare.isEmpty() )
687  {
688  int fieldIdx = mFieldsToPrepare.takeFirst();
689  if ( mPreparedFields.contains( fieldIdx ) )
690  continue;
691 
692  mPreparedFields << fieldIdx;
693  prepareField( fieldIdx );
694  }
695 
696  //sort joins by dependency
697  if ( !mFetchJoinInfo.empty() )
698  {
699  createOrderedJoinList();
700  }
701 }
702 
703 void QgsVectorLayerFeatureIterator::createOrderedJoinList()
704 {
705  mOrderedJoinInfoList = mFetchJoinInfo.values();
706  if ( mOrderedJoinInfoList.size() < 2 )
707  {
708  return;
709  }
710 
711  QSet<int> resolvedFields; //todo: get provider / virtual fields without joins
712 
713  //add all provider fields without joins as resolved fields
714  QList< int >::const_iterator prepFieldIt = mPreparedFields.constBegin();
715  for ( ; prepFieldIt != mPreparedFields.constEnd(); ++prepFieldIt )
716  {
717  if ( mSource->mFields.fieldOrigin( *prepFieldIt ) != QgsFields::OriginJoin )
718  {
719  resolvedFields.insert( *prepFieldIt );
720  }
721  }
722 
723  //iterate through the joins. If target field is not yet covered, move the entry to the end of the list
724 
725  //some join combinations might not have a resolution at all
726  int maxIterations = ( mOrderedJoinInfoList.size() + 1 ) * mOrderedJoinInfoList.size() / 2.0;
727  int currentIteration = 0;
728 
729  for ( int i = 0; i < mOrderedJoinInfoList.size() - 1; ++i )
730  {
731  if ( !resolvedFields.contains( mOrderedJoinInfoList.at( i ).targetField ) )
732  {
733  mOrderedJoinInfoList.append( mOrderedJoinInfoList.at( i ) );
734  mOrderedJoinInfoList.removeAt( i );
735  --i;
736  }
737  else
738  {
739  int offset = mOrderedJoinInfoList.at( i ).indexOffset;
740  int joinField = mOrderedJoinInfoList.at( i ).joinField;
741 
742  QgsAttributeList attributes = mOrderedJoinInfoList.at( i ).attributes;
743  for ( int n = 0; n < attributes.size(); n++ )
744  {
745  if ( n != joinField )
746  {
747  resolvedFields.insert( joinField < n ? n + offset - 1 : n + offset );
748  }
749  }
750  }
751 
752  ++currentIteration;
753  if ( currentIteration >= maxIterations )
754  {
755  break;
756  }
757  }
758 }
759 
760 bool QgsVectorLayerFeatureIterator::postProcessFeature( QgsFeature &feature )
761 {
762  bool result = checkGeometryValidity( feature );
763  if ( result )
765  return result;
766 }
767 
768 bool QgsVectorLayerFeatureIterator::checkGeometryValidity( const QgsFeature &feature )
769 {
770  if ( !feature.hasGeometry() )
771  return true;
772 
773  switch ( mRequest.invalidGeometryCheck() )
774  {
776  return true;
777 
779  {
780  if ( !feature.geometry().isGeosValid() )
781  {
782  QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::Critical );
784  {
785  mRequest.invalidGeometryCallback()( feature );
786  }
787  return false;
788  }
789  break;
790  }
791 
793  if ( !feature.geometry().isGeosValid() )
794  {
795  QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::Critical );
796  close();
798  {
799  mRequest.invalidGeometryCallback()( feature );
800  }
801  return false;
802  }
803  break;
804  }
805 
806  return true;
807 }
808 
810 {
811  switch ( mSource->mFields.fieldOrigin( fieldIdx ) )
812  {
814  prepareExpression( fieldIdx );
815  break;
816 
819  {
820  prepareJoin( fieldIdx );
821  }
822  break;
823 
827  break;
828  }
829 }
830 
832 {
833  QList< FetchJoinInfo >::const_iterator joinIt = mOrderedJoinInfoList.constBegin();
834  for ( ; joinIt != mOrderedJoinInfoList.constEnd(); ++joinIt )
835  {
836  QVariant targetFieldValue = f.attribute( joinIt->targetField );
837  if ( !targetFieldValue.isValid() )
838  continue;
839 
840  const QHash< QString, QgsAttributes> &memoryCache = joinIt->joinInfo->cachedAttributes;
841  if ( memoryCache.isEmpty() )
842  joinIt->addJoinedAttributesDirect( f, targetFieldValue );
843  else
844  joinIt->addJoinedAttributesCached( f, targetFieldValue );
845  }
846 }
847 
849 {
850  // make sure we have space for newly added attributes
851  QgsAttributes attr = f.attributes();
852  attr.resize( mSource->mFields.count() ); // Provider attrs count + joined attrs count + expression attrs count
853  f.setAttributes( attr );
854 
855  // possible TODO - handle combinations of expression -> join -> expression -> join?
856  // but for now, write that off as too complex and an unlikely rare, unsupported use case
857 
858  QList< int > fetchedVirtualAttributes;
859  //first, check through joins for any virtual fields we need
860  QMap<const QgsVectorLayerJoinInfo *, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
861  for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
862  {
863  if ( mExpressionFieldInfo.contains( joinIt->targetField ) )
864  {
865  // have to calculate expression field before we can handle this join
866  addExpressionAttribute( f, joinIt->targetField );
867  fetchedVirtualAttributes << joinIt->targetField;
868  }
869  }
870 
871  if ( !mFetchJoinInfo.isEmpty() )
872  addJoinedAttributes( f );
873 
874  // add remaining expression fields
875  if ( !mExpressionFieldInfo.isEmpty() )
876  {
877  QMap<int, QgsExpression *>::ConstIterator it = mExpressionFieldInfo.constBegin();
878  for ( ; it != mExpressionFieldInfo.constEnd(); ++it )
879  {
880  if ( fetchedVirtualAttributes.contains( it.key() ) )
881  continue;
882 
883  addExpressionAttribute( f, it.key() );
884  }
885  }
886 }
887 
889 {
890  QgsExpression *exp = mExpressionFieldInfo.value( attrIndex );
891  if ( exp )
892  {
893  mExpressionContext->setFeature( f );
894  QVariant val = exp->evaluate( mExpressionContext.get() );
895  ( void )mSource->mFields.at( attrIndex ).convertCompatible( val );
896  f.setAttribute( attrIndex, val );
897  }
898  else
899  {
900  f.setAttribute( attrIndex, QVariant() );
901  }
902 }
903 
905 {
906  Q_UNUSED( simplifyMethod )
907  return false;
908 }
909 
910 bool QgsVectorLayerFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
911 {
912  Q_UNUSED( methodType )
913  return false;
914 }
915 
916 
918 {
919  const QHash<QString, QgsAttributes> &memoryCache = joinInfo->cachedAttributes;
920  QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
921  if ( it == memoryCache.constEnd() )
922  return; // joined value not found -> leaving the attributes empty (null)
923 
924  int index = indexOffset;
925 
926  const QgsAttributes &featureAttributes = it.value();
927  for ( int i = 0; i < featureAttributes.count(); ++i )
928  {
929  f.setAttribute( index++, featureAttributes.at( i ) );
930  }
931 }
932 
933 
934 
936 {
937  // no memory cache, query the joined values by setting substring
938  QString subsetString;
939 
940  QString joinFieldName = joinInfo->joinFieldName();
941 
942  subsetString.append( QStringLiteral( "\"%1\"" ).arg( joinFieldName ) );
943 
944  if ( joinValue.isNull() )
945  {
946  subsetString += QLatin1String( " IS NULL" );
947  }
948  else
949  {
950  QString v = joinValue.toString();
951  switch ( joinValue.type() )
952  {
953  case QVariant::Int:
954  case QVariant::LongLong:
955  case QVariant::Double:
956  break;
957 
958  default:
959  case QVariant::String:
960  v.replace( '\'', QLatin1String( "''" ) );
961  v.prepend( '\'' ).append( '\'' );
962  break;
963  }
964  subsetString += '=' + v;
965  }
966 
967  // maybe user requested just a subset of layer's attributes
968  // so we do not have to cache everything
969  QVector<int> subsetIndices;
970  if ( joinInfo->hasSubset() )
971  {
972  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinInfo );
973  subsetIndices = QgsVectorLayerJoinBuffer::joinSubsetIndices( joinLayer, subsetNames );
974  }
975 
976  // select (no geometry)
977  QgsFeatureRequest request;
979  request.setSubsetOfAttributes( attributes );
980  request.setFilterExpression( subsetString );
981  request.setLimit( 1 );
982  QgsFeatureIterator fi = joinLayer->getFeatures( request );
983 
984  // get first feature
985  QgsFeature fet;
986  if ( fi.nextFeature( fet ) )
987  {
988  int index = indexOffset;
989  QgsAttributes attr = fet.attributes();
990  if ( joinInfo->hasSubset() )
991  {
992  for ( int i = 0; i < subsetIndices.count(); ++i )
993  f.setAttribute( index++, attr.at( subsetIndices.at( i ) ) );
994  }
995  else
996  {
997  // use all fields except for the one used for join (has same value as exiting field in target layer)
998  for ( int i = 0; i < attr.count(); ++i )
999  {
1000  if ( i == joinField )
1001  continue;
1002 
1003  f.setAttribute( index++, attr.at( i ) );
1004  }
1005  }
1006  }
1007  else
1008  {
1009  // no suitable join feature found, keeping empty (null) attributes
1010  }
1011 }
1012 
1013 
1014 
1015 
1017 {
1018  QgsFeatureId featureId = mRequest.filterFid();
1019 
1020  // deleted already?
1021  if ( mSource->mDeletedFeatureIds.contains( featureId ) )
1022  return false;
1023 
1024  // has changed geometry?
1025  if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) )
1026  {
1027  useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f );
1028  return true;
1029  }
1030 
1031  // added features
1032  for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter )
1033  {
1034  if ( iter->id() == featureId )
1035  {
1036  useAddedFeature( *iter, f );
1037  return true;
1038  }
1039  }
1040 
1041  // regular features
1043  if ( fi.nextFeature( f ) )
1044  {
1045  f.setFields( mSource->mFields );
1046 
1047  if ( mSource->mHasEditBuffer )
1049 
1050  if ( mHasVirtualAttributes )
1051  addVirtualAttributes( f );
1052 
1053  return true;
1054  }
1055 
1056  return false;
1057 }
1058 
1060 {
1061  QgsAttributes attrs = f.attributes();
1062 
1063  // remove all attributes that will disappear - from higher indices to lower
1064  for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
1065  {
1066  attrs.remove( mSource->mDeletedAttributeIds[idx] );
1067  }
1068 
1069  // adjust size to accommodate added attributes
1070  attrs.resize( attrs.count() + mSource->mAddedAttributes.count() );
1071 
1072  // update changed attributes
1073  if ( mSource->mChangedAttributeValues.contains( f.id() ) )
1074  {
1076  for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
1077  attrs[it.key()] = it.value();
1078  }
1079  f.setAttributes( attrs );
1080 }
1081 
1083 {
1084  if ( mSource->mChangedGeometries.contains( f.id() ) )
1086 }
1087 
1088 bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys )
1089 {
1090  Q_UNUSED( orderBys )
1091  return true;
1092 }
1093 
1094 
1095 //
1096 // QgsVectorLayerSelectedFeatureSource
1097 //
1098 
1100  : mSource( layer )
1101  , mSelectedFeatureIds( layer->selectedFeatureIds() )
1102  , mWkbType( layer->wkbType() )
1103  , mName( layer->name() )
1104  , mLayer( layer )
1105 {}
1106 
1108 {
1109  QgsFeatureRequest req( request );
1110 
1111  // while QgsVectorLayerSelectedFeatureIterator will reject any features not in mSelectedFeatureIds,
1112  // we still tweak the feature request to only request selected feature ids wherever we can -- this
1113  // allows providers to optimise the request and avoid requesting features we don't need
1114  // note that we can't do this for some request types - e.g. expression based requests, so
1115  // in that case we just pass the request on to the provider and let QgsVectorLayerSelectedFeatureIterator
1116  // do ALL the filtering
1117  if ( req.filterFids().isEmpty() && req.filterType() == QgsFeatureRequest::FilterNone )
1118  {
1119  req.setFilterFids( mSelectedFeatureIds );
1120  }
1121  else if ( !req.filterFids().isEmpty() )
1122  {
1123  QgsFeatureIds reqIds = mSelectedFeatureIds;
1124  reqIds.intersect( req.filterFids() );
1125  req.setFilterFids( reqIds );
1126  }
1127 
1128  return QgsFeatureIterator( new QgsVectorLayerSelectedFeatureIterator( mSelectedFeatureIds, req, mSource ) );
1129 }
1130 
1132 {
1133  return mSource.crs();
1134 }
1135 
1137 {
1138  return mSource.fields();
1139 }
1140 
1142 {
1143  return mWkbType;
1144 }
1145 
1147 {
1148  return mSelectedFeatureIds.count();
1149 }
1150 
1152 {
1153  return mName;
1154 }
1155 
1157 {
1158  if ( mLayer )
1159  return mLayer->createExpressionContextScope();
1160  else
1161  return nullptr;
1162 }
1163 
1164 //
1165 // QgsVectorLayerSelectedFeatureIterator
1166 //
1167 
1169 QgsVectorLayerSelectedFeatureIterator::QgsVectorLayerSelectedFeatureIterator( const QgsFeatureIds &selectedFeatureIds, const QgsFeatureRequest &request, QgsVectorLayerFeatureSource &source )
1170  : QgsAbstractFeatureIterator( request )
1171  , mSelectedFeatureIds( selectedFeatureIds )
1172 {
1173  QgsFeatureRequest sourceRequest = request;
1174  if ( sourceRequest.filterType() == QgsFeatureRequest::FilterExpression && sourceRequest.limit() > 0 )
1175  {
1176  // we can't pass the request limit to the provider here - otherwise the provider will
1177  // limit the number of returned features and may only return a bunch of matching features
1178  // which AREN'T in the selected feature set
1179  sourceRequest.setLimit( -1 );
1180  }
1181  mIterator = source.getFeatures( sourceRequest );
1182 }
1183 
1184 bool QgsVectorLayerSelectedFeatureIterator::rewind()
1185 {
1186  return mIterator.rewind();
1187 }
1188 
1189 bool QgsVectorLayerSelectedFeatureIterator::close()
1190 {
1191  return mIterator.close();
1192 }
1193 
1194 bool QgsVectorLayerSelectedFeatureIterator::fetchFeature( QgsFeature &f )
1195 {
1196  while ( mIterator.nextFeature( f ) )
1197  {
1198  if ( mSelectedFeatureIds.contains( f.id() ) )
1199  return true;
1200  }
1201  return false;
1202 }
1203 
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:111
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.
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.
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: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...
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:438
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:85
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.