QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
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
125
130
132{
133 return mId;
134}
135
136
139 , mFetchedFid( false )
140{
143
144 // prepare spatial filter geometries for optimal speed
145 // since the mDistanceWithin* constraint member variables are all in the DESTINATION CRS,
146 // we set all these upfront before any transformation to the source CRS is done.
147
148 switch ( mRequest.spatialFilterType() )
149 {
152 break;
153
156 {
157 // Note that regardless of whether or not we'll ultimately be able to handoff this check to the underlying provider,
158 // we still need these reference geometry constraints in the vector layer iterator as we need them to check against
159 // the features from the vector layer's edit buffer! (In other words, we cannot completely hand off responsibility for
160 // these checks to the provider and ignore them locally)
163 mDistanceWithinEngine->prepareGeometry();
165 }
166 break;
167 }
168
169 bool canDelegateLimitToProvider = true;
170 try
171 {
173 {
175 break;
176
178 // we have to disable any limit on the provider's request -- since that request may be returning features which are outside the
179 // distance tolerance, we'll have to fetch them all and then handle the limit check manually only after testing for the distance within constraint
180 canDelegateLimitToProvider = false;
181 break;
182 }
183
184 // mFilterRect is in the source CRS, so we set that now (after request transformation has been done)
186 }
187 catch ( QgsCsException & )
188 {
189 // can't reproject request filters
190 close();
191 return;
192 }
193
194 // check whether the order by clause(s) can be delegated to the provider
195 mDelegatedOrderByToProvider = !mSource->mHasEditBuffer;
196 if ( !mRequest.orderBy().isEmpty() )
197 {
198 QSet<int> attributeIndexes;
199 const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields );
200 for ( const int attrIndex : usedAttributeIndices )
201 {
203 mDelegatedOrderByToProvider = false;
204
205 attributeIndexes << attrIndex;
206 }
207
208 if ( ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes ) && !mDelegatedOrderByToProvider )
209 {
210 attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
211 mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
212 }
213 }
214
216 {
219
221 {
222 // ensure that all fields required for filter expressions are prepared
224 attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
225 mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
226 }
227 }
228
230
232
233 // by default provider's request is the same
235 // but we remove any destination CRS parameter - that is handled in QgsVectorLayerFeatureIterator,
236 // not at the provider level. Otherwise virtual fields depending on geometry would have incorrect
237 // values
239 {
242 }
243
244 if ( !mDelegatedOrderByToProvider )
245 {
247 }
248
249 if ( !canDelegateLimitToProvider )
250 {
252 }
253
255 {
256 // prepare list of attributes to match provider fields
257 QSet<int> providerSubset;
259 const int nPendingFields = mSource->mFields.count();
260 for ( const int attrIndex : subset )
261 {
262 if ( attrIndex < 0 || attrIndex >= nPendingFields )
263 continue;
265 providerSubset << mSource->mFields.fieldOriginIndex( attrIndex );
266 }
267
268 // This is done in order to be prepared to do fallback order bys
269 // and be sure we have the required columns.
270 // TODO:
271 // It would be nicer to first check if we can compile the order by
272 // and only modify the subset if we cannot.
273 if ( !mProviderRequest.orderBy().isEmpty() )
274 {
275 const auto usedAttributeIndices = mProviderRequest.orderBy().usedAttributeIndices( mSource->mFields );
276 for ( const int attrIndex : usedAttributeIndices )
277 {
278 providerSubset << attrIndex;
279 }
280 }
281
282 mProviderRequest.setSubsetOfAttributes( qgis::setToList( providerSubset ) );
283 }
284
286 {
287 const bool needsGeom = mProviderRequest.filterExpression()->needsGeometry();
288 const auto constReferencedColumns = mProviderRequest.filterExpression()->referencedColumns();
289 for ( const QString &field : constReferencedColumns )
290 {
291 const int idx = source->mFields.lookupField( field );
292
293 // If there are fields in the expression which are not of origin provider, the provider will not be able to filter based on them.
294 // In this case we disable the expression filter.
295 if ( source->mFields.fieldOrigin( idx ) != Qgis::FieldOrigin::Provider )
296 {
298 // can't limit at provider side
300 if ( needsGeom )
301 {
302 // have to get geometry from provider in order to evaluate expression on client
304 }
305 break;
306 }
307 }
308 }
309
310 if ( mSource->mHasEditBuffer )
311 {
313 QgsFeatureIds changedIds;
314 QgsChangedAttributesMap::const_iterator attIt = mSource->mChangedAttributeValues.constBegin();
315 for ( ; attIt != mSource->mChangedAttributeValues.constEnd(); ++attIt )
316 {
317 changedIds << attIt.key();
318 }
320
321 if ( mChangedFeaturesRequest.limit() > 0 )
322 {
323 int providerLimit = mProviderRequest.limit();
324
325 // features may be deleted in buffer, so increase limit sent to provider
326 providerLimit += mSource->mDeletedFeatureIds.size();
327
329 {
330 // attribute changes may mean some features no longer match expression, so increase limit sent to provider
331 providerLimit += mSource->mChangedAttributeValues.size();
332 }
333
335 {
336 // geometry changes may mean some features no longer match expression or rect, so increase limit sent to provider
337 providerLimit += mSource->mChangedGeometries.size();
338 }
339
340 mProviderRequest.setLimit( providerLimit );
341 mChangedFeaturesRequest.setLimit( providerLimit );
342 }
343 }
344
346 {
347 mFetchedFid = false;
348 }
349 else // no filter or filter by rect
350 {
352 {
353 if ( mSource->mHasEditBuffer )
354 {
356 }
357 else
358 {
360 }
361 }
362
364 }
365}
366
367
374
376
382class QgsThreadStackOverflowGuard
383{
384 public:
385
386#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
387 QgsThreadStackOverflowGuard( QThreadStorage<std::deque<QString>> &storage, const QString &stackFrameInformation, int maxDepth )
388#else
389 QgsThreadStackOverflowGuard( std::deque<QString> &storage, const QString &stackFrameInformation, int maxDepth )
390#endif
391 : mStorage( storage )
392 , mMaxDepth( maxDepth )
393 {
394#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
395 if ( !storage.hasLocalData() )
396 {
397 storage.setLocalData( std::deque<QString>() );
398 }
399
400 storage.localData().emplace_back( stackFrameInformation );
401#else
402 storage.emplace_back( stackFrameInformation );
403#endif
404 }
405
406 ~QgsThreadStackOverflowGuard()
407 {
408#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
409 mStorage.localData().pop_back();
410#else
411 mStorage.pop_back();
412#endif
413 }
414
415 bool hasStackOverflow() const
416 {
417#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
418 if ( mStorage.localData().size() > mMaxDepth )
419#else
420 if ( mStorage.size() > mMaxDepth )
421#endif
422 return true;
423 else
424 return false;
425 }
426
427 QString topFrames() const
428 {
429 QStringList dumpStack;
430#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
431 const std::deque<QString> &stack = mStorage.localData();
432#else
433 const std::deque<QString> &stack = mStorage;
434#endif
435
436 const int dumpSize = std::min( static_cast<int>( stack.size() ), 10 );
437 auto stackIt = stack.begin();
438 for ( int i = 0; i < dumpSize; ++i, stackIt++ )
439 {
440 dumpStack += *stackIt;
441 }
442
443 return dumpStack.join( '\n' );
444 }
445
446 std::size_t depth() const
447 {
448#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
449 return mStorage.localData().size();
450#else
451 return mStorage.size();
452#endif
453 }
454
455 private:
456#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
457 QThreadStorage<std::deque<QString>> &mStorage;
458#else
459 std::deque<QString> &mStorage;
460#endif
461 std::size_t mMaxDepth;
462};
463
465
467{
468 f.setValid( false );
469
470 if ( mClosed )
471 return false;
472
473#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
474 static QThreadStorage<std::deque<QString>> sStack;
475#else
476 static thread_local std::deque<QString> sStack;
477#endif
478
479 const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
480
481 if ( guard.hasStackOverflow() )
482 {
483 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 );
484 return false;
485 }
486
488 {
489 if ( mFetchedFid )
490 return false;
491 const bool res = nextFeatureFid( f );
492 if ( res && postProcessFeature( f ) )
493 {
494 mFetchedFid = true;
495 return res;
496 }
497 else
498 {
499 return false;
500 }
501 }
502
503 if ( !mFilterRect.isNull() )
504 {
506 return true;
507
508 // no more changed geometries
509 }
510
512 {
514 return true;
515
517 return true;
518
519 // no more changed features
520 }
521
522 while ( fetchNextAddedFeature( f ) )
523 {
524 return true;
525 }
526 // no more added features
527
529 {
532 {
534 mProviderIterator.setInterruptionChecker( mInterruptionChecker );
535 }
536 }
537
538 while ( mProviderIterator.nextFeature( f ) )
539 {
540 if ( mFetchConsidered.contains( f.id() ) )
541 continue;
542
543 // TODO[MD]: just one resize of attributes
545
546 // update attributes
547 if ( mSource->mHasEditBuffer )
549
552 else
554
556 {
557 //filtering by expression, and couldn't do it on the provider side
560 {
561 //feature did not match filter
562 continue;
563 }
564 }
565
566 // update geometry
567 // TODO[MK]: FilterRect check after updating the geometry
570
571 if ( !postProcessFeature( f ) )
572 continue;
573
574 return true;
575 }
576 // no more provider features
577
578 close();
579 return false;
580}
581
582
583
585{
586 if ( mClosed )
587 return false;
588
590 {
591 mFetchedFid = false;
592 }
593 else
594 {
597 }
598
599 return true;
600}
601
603{
604 if ( mClosed )
605 return false;
606
608
610
611 mClosed = true;
612 return true;
613}
614
616{
617 mProviderIterator.setInterruptionChecker( interruptionChecker );
618 mInterruptionChecker = interruptionChecker;
619}
620
625
627{
628 while ( mFetchAddedFeaturesIt != mSource->mAddedFeatures.constBegin() )
629 {
631 const QgsFeatureId fid = mFetchAddedFeaturesIt->id();
632
633 if ( mFetchConsidered.contains( fid ) )
634 // must have changed geometry outside rectangle
635 continue;
636
638
639 // can't test for feature acceptance until after calling useAddedFeature
640 // since acceptFeature may rely on virtual fields
641 if ( !mRequest.acceptFeature( f ) )
642 // skip features which are not accepted by the filter
643 continue;
644
645 if ( !postProcessFeature( f ) )
646 continue;
647
648 return true;
649 }
650
652 return false; // no more added features
653}
654
655
657{
658 // since QgsFeature is implicitly shared, it's more efficient to just copy the
659 // whole feature, even if flags like NoGeometry or a subset of attributes is set at the request.
660 // This helps potentially avoid an unnecessary detach of the feature
661 f = src;
662 f.setValid( true );
664
667 else
669}
670
671
672
674{
675 // check if changed geometries are in rectangle
677 {
678 const QgsFeatureId fid = mFetchChangedGeomIt.key();
679
680 if ( mFetchConsidered.contains( fid ) )
681 // skip deleted features
682 continue;
683
684 mFetchConsidered << fid;
685
686 if ( !mFilterRect.isNull() && !mFetchChangedGeomIt->intersects( mFilterRect ) )
687 // skip changed geometries not in rectangle and don't check again
688 continue;
689
691
693 {
696 {
697 continue;
698 }
699 }
700
701 if ( postProcessFeature( f ) )
702 {
703 // return complete feature
705 return true;
706 }
707 }
708
709 return false; // no more changed geometries
710}
711
713{
715 {
716 if ( mFetchConsidered.contains( f.id() ) )
717 continue;
718
719 mFetchConsidered << f.id();
720
722
723 // also update geometry if needed
724 const auto changedGeometryIt = mSource->mChangedGeometries.constFind( f.id() );
725 if ( changedGeometryIt != mSource->mChangedGeometries.constEnd() )
726 {
727 if ( !mFilterRect.isNull() && !changedGeometryIt->intersects( mFilterRect ) )
728 // skip changed geometries not in rectangle and don't check again
729 continue;
730
731 f.setGeometry( *changedGeometryIt );
732 }
733
736
738 if ( mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() && postProcessFeature( f ) )
739 {
740 return true;
741 }
742 }
743
744 return false;
745}
746
747
749{
750 f.setId( fid );
751 f.setValid( true );
753
756 {
757 f.setGeometry( geom );
758 }
759
760 const bool subsetAttrs = ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes );
761 if ( !subsetAttrs || !mRequest.subsetOfAttributes().isEmpty() )
762 {
763 // retrieve attributes from provider
764 QgsFeature tmp;
765 //mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes );
766 QgsFeatureRequest request;
768 if ( subsetAttrs )
769 {
771 }
772 QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( request );
773 if ( fi.nextFeature( tmp ) )
774 {
777 f.setAttributes( tmp.attributes() );
778 }
779 }
780
782}
783
784
785
793
795{
796 if ( !mSource->mFields.exists( fieldIdx ) )
797 return;
798
800 return;
801
802 int sourceLayerIndex;
803 const QgsVectorLayerJoinInfo *joinInfo = mSource->mJoinBuffer->joinForFieldIndex( fieldIdx, mSource->mFields, sourceLayerIndex );
804 Q_ASSERT( joinInfo );
805
806 auto joinSourceIt = mSource->mJoinSources.constFind( joinInfo->joinLayerId() );
807 if ( joinSourceIt == mSource->mJoinSources.constEnd() )
808 return; // invalid join (unresolved reference to layer)
809
810 if ( !mFetchJoinInfo.contains( joinInfo ) )
811 {
812 FetchJoinInfo info;
813 info.joinInfo = joinInfo;
814 info.joinSource = joinSourceIt->joinSource;
815 info.indexOffset = mSource->mJoinBuffer->joinedFieldsOffset( joinInfo, mSource->mFields );
817 info.joinField = joinSourceIt->joinLayerFields.indexFromName( joinInfo->joinFieldName() );
818 info.joinLayerFields = joinSourceIt->joinLayerFields;
819
820 // for joined fields, we always need to request the targetField from the provider too
821 if ( !mPreparedFields.contains( info.targetField ) && !mFieldsToPrepare.contains( info.targetField ) )
822 mFieldsToPrepare << info.targetField;
823
826
827 mFetchJoinInfo.insert( joinInfo, info );
828 }
829
830 // store field source index - we'll need it when fetching from provider
831 mFetchJoinInfo[ joinInfo ].attributes.push_back( sourceLayerIndex );
832 mFetchJoinInfo[ joinInfo ].attributesSourceToDestLayerMap[sourceLayerIndex] = fieldIdx;
833}
834
835
837{
838#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
839 static QThreadStorage<std::deque<QString>> sStack;
840#else
841 static thread_local std::deque<QString> sStack;
842#endif
843
844 const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
845
846 if ( guard.hasStackOverflow() )
847 {
848 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 );
849 return;
850 }
851
852 const QList<QgsExpressionFieldBuffer::ExpressionField> &exps = mSource->mExpressionFieldBuffer->expressions();
853
854 const int oi = mSource->mFields.fieldOriginIndex( fieldIdx );
855 std::unique_ptr<QgsExpression> exp = std::make_unique<QgsExpression>( exps[oi].cachedExpression );
856
858 da.setSourceCrs( mSource->mCrs, QgsProject::instance()->transformContext() ); // skip-keyword-check
859 da.setEllipsoid( QgsProject::instance()->ellipsoid() ); // skip-keyword-check
860 exp->setGeomCalculator( &da );
861 exp->setDistanceUnits( QgsProject::instance()->distanceUnits() ); // skip-keyword-check
862 exp->setAreaUnits( QgsProject::instance()->areaUnits() ); // skip-keyword-check
863
864 if ( !mExpressionContext )
865 createExpressionContext();
866 exp->prepare( mExpressionContext.get() );
867 const QSet<int> referencedColumns = exp->referencedAttributeIndexes( mSource->fields() );
868
869 QSet<int> requestedAttributes = qgis::listToSet( mRequest.subsetOfAttributes() );
870
871 for ( const int dependentFieldIdx : referencedColumns )
872 {
874 {
875 requestedAttributes += dependentFieldIdx;
876 }
877 // also need to fetch this dependent field
878 if ( !mPreparedFields.contains( dependentFieldIdx ) && !mFieldsToPrepare.contains( dependentFieldIdx ) )
879 mFieldsToPrepare << dependentFieldIdx;
880 }
881
883 {
884 mRequest.setSubsetOfAttributes( qgis::setToList( requestedAttributes ) );
885 }
886
887 if ( exp->needsGeometry() )
888 {
890 }
891
892 mExpressionFieldInfo.insert( fieldIdx, exp.release() );
893}
894
896{
897 mPreparedFields.clear();
898 mFieldsToPrepare.clear();
899 mFetchJoinInfo.clear();
900 mOrderedJoinInfoList.clear();
901
902 mExpressionContext.reset();
903
907
908 while ( !mFieldsToPrepare.isEmpty() )
909 {
910 const int fieldIdx = mFieldsToPrepare.takeFirst();
911 if ( mPreparedFields.contains( fieldIdx ) )
912 continue;
913
914 mPreparedFields << fieldIdx;
915 prepareField( fieldIdx );
916 }
917
918 //sort joins by dependency
919 if ( !mFetchJoinInfo.empty() )
920 {
921 createOrderedJoinList();
922 }
923
924}
925
926void QgsVectorLayerFeatureIterator::createOrderedJoinList()
927{
928 mOrderedJoinInfoList = mFetchJoinInfo.values();
929 if ( mOrderedJoinInfoList.size() < 2 )
930 {
931 return;
932 }
933
934 QSet<int> resolvedFields; //todo: get provider / virtual fields without joins
935
936 //add all provider fields without joins as resolved fields
937 QList< int >::const_iterator prepFieldIt = mPreparedFields.constBegin();
938 for ( ; prepFieldIt != mPreparedFields.constEnd(); ++prepFieldIt )
939 {
940 if ( mSource->mFields.fieldOrigin( *prepFieldIt ) != Qgis::FieldOrigin::Join )
941 {
942 resolvedFields.insert( *prepFieldIt );
943 }
944 }
945
946 //iterate through the joins. If target field is not yet covered, move the entry to the end of the list
947
948 //some join combinations might not have a resolution at all
949 const int maxIterations = ( mOrderedJoinInfoList.size() + 1 ) * mOrderedJoinInfoList.size() / 2.0;
950 int currentIteration = 0;
951
952 for ( int i = 0; i < mOrderedJoinInfoList.size() - 1; ++i )
953 {
954 if ( !resolvedFields.contains( mOrderedJoinInfoList.at( i ).targetField ) )
955 {
956 mOrderedJoinInfoList.append( mOrderedJoinInfoList.at( i ) );
957 mOrderedJoinInfoList.removeAt( i );
958 --i;
959 }
960 else
961 {
962 const int offset = mOrderedJoinInfoList.at( i ).indexOffset;
963 const int joinField = mOrderedJoinInfoList.at( i ).joinField;
964
965 const QgsAttributeList attributes = mOrderedJoinInfoList.at( i ).attributes;
966 for ( int n = 0; n < attributes.size(); n++ )
967 {
968 if ( n != joinField )
969 {
970 resolvedFields.insert( joinField < n ? n + offset - 1 : n + offset );
971 }
972 }
973 }
974
975 ++currentIteration;
976 if ( currentIteration >= maxIterations )
977 {
978 break;
979 }
980 }
981}
982
983bool QgsVectorLayerFeatureIterator::postProcessFeature( QgsFeature &feature )
984{
985 bool result = checkGeometryValidity( feature );
986 if ( result && mHasValidTransform )
988
989 if ( result && mDistanceWithinEngine && feature.hasGeometry() )
990 {
991 result = mDistanceWithinEngine->distanceWithin( feature.geometry().constGet(), mDistanceWithin );
992 }
993
994 return result;
995}
996
997bool QgsVectorLayerFeatureIterator::checkGeometryValidity( const QgsFeature &feature )
998{
999 switch ( mRequest.invalidGeometryCheck() )
1000 {
1002 return true;
1003
1005 {
1006 if ( !feature.hasGeometry() )
1007 return true;
1008
1009 if ( !feature.geometry().isGeosValid() )
1010 {
1011 QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
1013 {
1014 mRequest.invalidGeometryCallback()( feature );
1015 }
1016 return false;
1017 }
1018 break;
1019 }
1020
1022 if ( !feature.hasGeometry() )
1023 return true;
1024
1025 if ( !feature.geometry().isGeosValid() )
1026 {
1027 QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
1028 close();
1030 {
1031 mRequest.invalidGeometryCallback()( feature );
1032 }
1033 return false;
1034 }
1035 break;
1036 }
1037
1038 return true;
1039}
1040
1042{
1043 switch ( mSource->mFields.fieldOrigin( fieldIdx ) )
1044 {
1046 prepareExpression( fieldIdx );
1047 break;
1048
1050 if ( mSource->mJoinBuffer->containsJoins() )
1051 {
1052 prepareJoin( fieldIdx );
1053 }
1054 break;
1055
1059 break;
1060 }
1061}
1062
1064{
1065 QList< FetchJoinInfo >::const_iterator joinIt = mOrderedJoinInfoList.constBegin();
1066 for ( ; joinIt != mOrderedJoinInfoList.constEnd(); ++joinIt )
1067 {
1068 const QVariant targetFieldValue = f.attribute( joinIt->targetField );
1069 if ( !targetFieldValue.isValid() )
1070 continue;
1071
1072 const QHash< QString, QgsAttributes> &memoryCache = joinIt->joinInfo->cachedAttributes;
1073 if ( memoryCache.isEmpty() )
1074 joinIt->addJoinedAttributesDirect( f, targetFieldValue );
1075 else
1076 joinIt->addJoinedAttributesCached( f, targetFieldValue );
1077 }
1078}
1079
1081{
1082 // make sure we have space for newly added attributes
1083 QgsAttributes attr = f.attributes();
1084 attr.resize( mSource->mFields.count() ); // Provider attrs count + joined attrs count + expression attrs count
1085 f.setAttributes( attr );
1086
1087 // possible TODO - handle combinations of expression -> join -> expression -> join?
1088 // but for now, write that off as too complex and an unlikely rare, unsupported use case
1089
1090 QList< int > fetchedVirtualAttributes;
1091 //first, check through joins for any virtual fields we need
1092 QMap<const QgsVectorLayerJoinInfo *, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
1093 for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
1094 {
1095 if ( mExpressionFieldInfo.contains( joinIt->targetField ) )
1096 {
1097 // have to calculate expression field before we can handle this join
1098 addExpressionAttribute( f, joinIt->targetField );
1099 fetchedVirtualAttributes << joinIt->targetField;
1100 }
1101 }
1102
1103 if ( !mFetchJoinInfo.isEmpty() )
1105
1106 // add remaining expression fields
1107 if ( !mExpressionFieldInfo.isEmpty() )
1108 {
1109 QMap<int, QgsExpression *>::ConstIterator it = mExpressionFieldInfo.constBegin();
1110 for ( ; it != mExpressionFieldInfo.constEnd(); ++it )
1111 {
1112 if ( fetchedVirtualAttributes.contains( it.key() ) )
1113 continue;
1114
1115 addExpressionAttribute( f, it.key() );
1116 }
1117 }
1118}
1119
1121{
1122 QgsExpression *exp = mExpressionFieldInfo.value( attrIndex );
1123 if ( exp )
1124 {
1125 if ( !mExpressionContext )
1126 createExpressionContext();
1127
1128 mExpressionContext->setFeature( f );
1129 QVariant val = exp->evaluate( mExpressionContext.get() );
1130 ( void )mSource->mFields.at( attrIndex ).convertCompatible( val );
1131 f.setAttribute( attrIndex, val );
1132 }
1133 else
1134 {
1135 f.setAttribute( attrIndex, QVariant() );
1136 }
1137}
1138
1140{
1141 Q_UNUSED( simplifyMethod )
1142 return false;
1143}
1144
1145bool QgsVectorLayerFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
1146{
1147 Q_UNUSED( methodType )
1148 return false;
1149}
1150
1151
1153{
1154 const QHash<QString, QgsAttributes> &memoryCache = joinInfo->cachedAttributes;
1155 const QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
1156 if ( it == memoryCache.constEnd() )
1157 return; // joined value not found -> leaving the attributes empty (null)
1158
1159 int index = indexOffset;
1160
1161 const QgsAttributes &featureAttributes = it.value();
1162 for ( int i = 0; i < featureAttributes.count(); ++i )
1163 {
1164 f.setAttribute( index++, featureAttributes.at( i ) );
1165 }
1166}
1167
1168
1169
1171{
1172#if 0 // this is not thread safe -- we cannot access the layer here as this will be called from non-main threads.
1173 // Shortcut
1174 if ( joinLayer && ! joinLayer->hasFeatures() )
1175 {
1176 return;
1177 }
1178#endif
1179
1180 // no memory cache, query the joined values by setting substring
1181 QString subsetString;
1182
1183 const QString joinFieldName = joinInfo->joinFieldName();
1184
1185 subsetString.append( QStringLiteral( "\"%1\"" ).arg( joinFieldName ) );
1186
1187 if ( QgsVariantUtils::isNull( joinValue ) )
1188 {
1189 subsetString += QLatin1String( " IS NULL" );
1190 }
1191 else
1192 {
1193 QString v = joinValue.toString();
1194 switch ( joinValue.userType() )
1195 {
1196 case QMetaType::Type::Int:
1197 case QMetaType::Type::LongLong:
1198 case QMetaType::Type::Double:
1199 break;
1200
1201 default:
1202 case QMetaType::Type::QString:
1203 v.replace( '\'', QLatin1String( "''" ) );
1204 v.prepend( '\'' ).append( '\'' );
1205 break;
1206 }
1207 subsetString += '=' + v;
1208 }
1209
1210 QList<int> joinedAttributeIndices;
1211
1212 // maybe user requested just a subset of layer's attributes
1213 // so we do not have to cache everything
1214 if ( joinInfo->hasSubset() )
1215 {
1216 const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinInfo, joinLayerFields );
1217 const QVector<int> subsetIndices = QgsVectorLayerJoinBuffer::joinSubsetIndices( joinLayerFields, subsetNames );
1218 joinedAttributeIndices = qgis::setToList( qgis::listToSet( attributes ).intersect( qgis::listToSet( subsetIndices.toList() ) ) );
1219 }
1220 else
1221 {
1222 joinedAttributeIndices = attributes;
1223 }
1224
1225 // we don't need the join field, it is already present in the other table
1226 joinedAttributeIndices.removeAll( joinField );
1227
1228 // select (no geometry)
1229 QgsFeatureRequest request;
1231 request.setSubsetOfAttributes( joinedAttributeIndices );
1232 request.setFilterExpression( subsetString );
1233 request.setLimit( 1 );
1234 QgsFeatureIterator fi = joinSource->getFeatures( request );
1235
1236 // get first feature
1237 const QList<int> sourceAttrIndexes = attributesSourceToDestLayerMap.keys();
1238 QgsFeature fet;
1239 if ( fi.nextFeature( fet ) )
1240 {
1241 const QgsAttributes attr = fet.attributes();
1242
1243 for ( const int sourceAttrIndex : sourceAttrIndexes )
1244 {
1245 if ( sourceAttrIndex == joinField )
1246 continue;
1247
1248 const int destAttrIndex = attributesSourceToDestLayerMap.value( sourceAttrIndex );
1249
1250 f.setAttribute( destAttrIndex, attr.at( sourceAttrIndex ) );
1251 }
1252 }
1253 else
1254 {
1255 // no suitable join feature found, keeping empty (null) attributes
1256 }
1257}
1258
1259
1260
1261
1263{
1264 const QgsFeatureId featureId = mRequest.filterFid();
1265
1266 // deleted already?
1267 if ( mSource->mDeletedFeatureIds.contains( featureId ) )
1268 return false;
1269
1270 // has changed geometry?
1271 if ( !( mRequest.flags() & Qgis::FeatureRequestFlag::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) )
1272 {
1273 useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f );
1274 return true;
1275 }
1276
1277 // added features
1278 for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter )
1279 {
1280 if ( iter->id() == featureId )
1281 {
1282 useAddedFeature( *iter, f );
1283 return true;
1284 }
1285 }
1286
1287 // regular features
1289 if ( fi.nextFeature( f ) )
1290 {
1292
1293 if ( mSource->mHasEditBuffer )
1295
1298 else
1300
1301 return true;
1302 }
1303
1304 return false;
1305}
1306
1308{
1309 QgsAttributes attrs = f.attributes();
1310
1311 // remove all attributes that will disappear - from higher indices to lower
1312 for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
1313 {
1314 attrs.remove( mSource->mDeletedAttributeIds[idx] );
1315 }
1316
1317 // adjust size to accommodate added attributes
1318 attrs.resize( attrs.count() + mSource->mAddedAttributes.count() );
1319
1320 // update changed attributes
1321 if ( mSource->mChangedAttributeValues.contains( f.id() ) )
1322 {
1324 for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
1325 attrs[it.key()] = it.value();
1326 }
1327 f.setAttributes( attrs );
1328}
1329
1335
1336void QgsVectorLayerFeatureIterator::createExpressionContext()
1337{
1338 mExpressionContext = std::make_unique< QgsExpressionContext >();
1339 mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
1340 mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) ); // skip-keyword-check
1341 mExpressionContext->appendScope( new QgsExpressionContextScope( mSource->mLayerScope ) );
1342 mExpressionContext->setFeedback( mRequest.feedback() );
1343}
1344
1345bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys )
1346{
1347 Q_UNUSED( orderBys )
1348 return mDelegatedOrderByToProvider;
1349}
1350
1351
1352//
1353// QgsVectorLayerSelectedFeatureSource
1354//
1355
1357 : mSource( layer )
1358 , mSelectedFeatureIds( layer->selectedFeatureIds() )
1359 , mWkbType( layer->wkbType() )
1360 , mName( layer->name() )
1361 , mLayer( layer )
1362{}
1363
1365{
1366 QgsFeatureRequest req( request );
1367
1368 // while QgsVectorLayerSelectedFeatureIterator will reject any features not in mSelectedFeatureIds,
1369 // we still tweak the feature request to only request selected feature ids wherever we can -- this
1370 // allows providers to optimise the request and avoid requesting features we don't need
1371 // note that we can't do this for some request types - e.g. expression based requests, so
1372 // in that case we just pass the request on to the provider and let QgsVectorLayerSelectedFeatureIterator
1373 // do ALL the filtering
1374 if ( req.filterFids().isEmpty() && req.filterType() == Qgis::FeatureRequestFilterType::NoFilter )
1375 {
1376 req.setFilterFids( mSelectedFeatureIds );
1377 }
1378 else if ( !req.filterFids().isEmpty() )
1379 {
1380 QgsFeatureIds reqIds = mSelectedFeatureIds;
1381 reqIds.intersect( req.filterFids() );
1382 req.setFilterFids( reqIds );
1383 }
1384
1385 return QgsFeatureIterator( new QgsVectorLayerSelectedFeatureIterator( mSelectedFeatureIds, req, mSource ) );
1386}
1387
1392
1394{
1395 return mSource.fields();
1396}
1397
1399{
1400 return mWkbType;
1401}
1402
1404{
1405 return mSelectedFeatureIds.count();
1406}
1407
1409{
1410 return mName;
1411}
1412
1414{
1415 if ( mLayer )
1416 return mLayer->createExpressionContextScope();
1417 else
1418 return nullptr;
1419}
1420
1422{
1423 if ( mLayer )
1424 return mLayer->hasSpatialIndex();
1425 else
1427}
1428
1429//
1430// QgsVectorLayerSelectedFeatureIterator
1431//
1432
1434QgsVectorLayerSelectedFeatureIterator::QgsVectorLayerSelectedFeatureIterator( const QgsFeatureIds &selectedFeatureIds, const QgsFeatureRequest &request, QgsVectorLayerFeatureSource &source )
1435 : QgsAbstractFeatureIterator( request )
1436 , mSelectedFeatureIds( selectedFeatureIds )
1437{
1438 QgsFeatureRequest sourceRequest = request;
1439 if ( sourceRequest.filterType() == Qgis::FeatureRequestFilterType::Expression && sourceRequest.limit() > 0 )
1440 {
1441 // we can't pass the request limit to the provider here - otherwise the provider will
1442 // limit the number of returned features and may only return a bunch of matching features
1443 // which AREN'T in the selected feature set
1444 sourceRequest.setLimit( -1 );
1445 }
1446 mIterator = source.getFeatures( sourceRequest );
1447}
1448
1449bool QgsVectorLayerSelectedFeatureIterator::rewind()
1450{
1451 return mIterator.rewind();
1452}
1453
1454bool QgsVectorLayerSelectedFeatureIterator::close()
1455{
1456 return mIterator.close();
1457}
1458
1459bool QgsVectorLayerSelectedFeatureIterator::fetchFeature( QgsFeature &f )
1460{
1461 while ( mIterator.nextFeature( f ) )
1462 {
1463 if ( mSelectedFeatureIds.contains( f.id() ) )
1464 return true;
1465 }
1466 return false;
1467}
1468
@ Fid
Filter using feature ID.
@ Expression
Filter using expression.
@ NoFilter
No filter is applied.
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:522
@ 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.
@ Critical
Critical/error message.
Definition qgis.h:157
@ DistanceWithin
Filter by distance to reference geometry.
@ BoundingBox
Filter using a bounding box.
@ NoFilter
No spatial filtering of features.
@ Provider
Field originates from the underlying data provider of the vector layer.
@ Edit
Field has been temporarily added in editing mode.
@ Unknown
The field origin has not been specified.
@ Expression
Field is calculated from an expression.
@ Join
Field originates from a joined layer.
@ 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:256
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.
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.
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.
QgsFeatureRequest & setCoordinateTransform(const QgsCoordinateTransform &transform)
Sets the coordinate transform which will be used to transform the feature's geometries.
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...
QgsCoordinateTransform coordinateTransform() const
Returns the coordinate transform which will be used to transform the feature's geometries.
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...
QgsCoordinateTransform calculateTransform(const QgsCoordinateReferenceSystem &sourceCrs) const
Calculates the coordinate transform to use to transform geometries when they are originally in source...
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:58
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
QgsAttributes attributes
Definition qgsfeature.h:67
QgsFeatureId id
Definition qgsfeature.h:66
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
int attributeCount() const
Returns the number of attributes attached to the feature.
void padAttributes(int count)
Resizes the attributes attached to this feature by appending the specified count of NULL values to th...
void setId(QgsFeatureId id)
Sets the feature id for this feature.
QgsGeometry geometry
Definition qgsfeature.h:69
void setValid(bool validity)
Sets the validity of the feature.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
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:473
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Qgis::FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
Q_INVOKABLE bool exists(int i) const
Returns if a field index is valid.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
int fieldOriginIndex(int fieldIdx) const
Returns the field's origin index (its meaning is specific to each type of origin).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
A geometry is the spatial representation of a feature.
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:83
QString id
Definition qgsmaplayer.h:79
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.
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:113
bool isNull() const
Test if the rectangle is null (holding no spatial information).
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)
bool fetchFeature(QgsFeature &feature) override
fetch next feature, return true on success
std::shared_ptr< QgsGeometryEngine > mDistanceWithinEngine
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.
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
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
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.