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