QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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 ***************************************************************************/
15#include "qgsconfig.h"
17
18#include "qgsdistancearea.h"
19#include "qgsexception.h"
23#include "qgsgeometryengine.h"
25#include "qgsmessagelog.h"
26#include "qgsproject.h"
27#include "qgssimplifymethod.h"
29#include "qgsvectorlayer.h"
32
33#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
34#include <QThreadStorage>
35#endif
36
37#include <deque>
38#include <memory>
39
41{
42 const QMutexLocker locker( &layer->mFeatureSourceConstructorMutex );
43 const QgsVectorDataProvider *provider = layer->dataProvider();
44 if ( provider )
45 mProviderFeatureSource.reset( provider->featureSource() );
46 mFields = layer->fields();
47 mId = layer->id();
48
49 // update layer's join caches if necessary
50 if ( layer->mJoinBuffer->containsJoins() )
51 layer->mJoinBuffer->createJoinCaches();
52
53 mJoinBuffer.reset( layer->mJoinBuffer->clone() );
54 for ( const QgsVectorLayerJoinInfo &joinInfo : mJoinBuffer->vectorJoins() )
55 {
56 if ( QgsVectorLayer *joinLayer = joinInfo.joinLayer() )
57 {
58 JoinLayerSource source;
59 source.joinSource = std::make_shared< QgsVectorLayerFeatureSource >( joinLayer );
60 source.joinLayerFields = joinLayer->fields();
61 mJoinSources.insert( joinLayer->id(), source );
62 }
63 }
64
65 mExpressionFieldBuffer = std::make_unique<QgsExpressionFieldBuffer>( *layer->mExpressionFieldBuffer );
66 mCrs = layer->crs();
67
68 mHasEditBuffer = layer->editBuffer();
69 if ( mHasEditBuffer )
70 {
71#if 0
72 // TODO[MD]: after merge
73 if ( request.filterType() == QgsFeatureRequest::FilterFid )
74 {
75
76 // only copy relevant parts
77 if ( L->editBuffer()->addedFeatures().contains( request.filterFid() ) )
78 mAddedFeatures.insert( request.filterFid(), L->editBuffer()->addedFeatures()[ request.filterFid()] );
79
80 if ( L->editBuffer()->changedGeometries().contains( request.filterFid() ) )
81 mChangedGeometries.insert( request.filterFid(), L->editBuffer()->changedGeometries()[ request.filterFid()] );
82
83 if ( L->editBuffer()->deletedFeatureIds().contains( request.filterFid() ) )
84 mDeletedFeatureIds.insert( request.filterFid() );
85
86 if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
87 mChangedAttributeValues.insert( request.filterFid(), L->editBuffer()->changedAttributeValues()[ request.filterFid()] );
88
89 if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
90 mChangedFeaturesRequest.setFilterFids( QgsFeatureIds() << request.filterFid() );
91 }
92 else
93 {
94#endif
95 // If we are inside a transaction the iterator "sees" the current status
96 if ( provider && ! provider->transaction() )
97 {
102 mAddedAttributes = QList<QgsField>( layer->editBuffer()->addedAttributes() );
104 }
105#if 0
106 }
107#endif
108 }
109
110 const std::unique_ptr< QgsExpressionContextScope > layerScope( QgsExpressionContextUtils::layerScope( layer ) );
111 mLayerScope = *layerScope;
112}
113
115
117{
118 // return feature iterator that does not own this source
119 return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, false, request ) );
120}
121
126
131
133{
134 return mId;
135}
136
137
140{
141 mTransform = mRequest.calculateTransform( mSource->mCrs );
142 mHasValidTransform = mTransform.isValid();
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 {
150 case Qgis::SpatialFilterType::NoFilter:
151 case Qgis::SpatialFilterType::BoundingBox:
152 break;
153
154 case Qgis::SpatialFilterType::DistanceWithin:
155 if ( !mRequest.referenceGeometry().isEmpty() )
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)
161 mDistanceWithinGeom = mRequest.referenceGeometry();
162 mDistanceWithinEngine = mRequest.referenceGeometryEngine();
163 mDistanceWithinEngine->prepareGeometry();
164 mDistanceWithin = mRequest.distanceWithin();
165 }
166 break;
167 }
168
169 bool canDelegateLimitToProvider = true;
170 try
171 {
172 switch ( updateRequestToSourceCrs( mRequest, mTransform ) )
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)
185 mFilterRect = mRequest.filterRect();
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 {
202 if ( mSource->mFields.fieldOrigin( attrIndex ) != Qgis::FieldOrigin::Provider )
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
215 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
216 {
217 mRequest.expressionContext()->setFields( mSource->mFields );
218 mRequest.filterExpression()->prepare( mRequest.expressionContext() );
219
220 if ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes )
221 {
222 // ensure that all fields required for filter expressions are prepared
223 QSet<int> attributeIndexes = mRequest.filterExpression()->referencedAttributeIndexes( mSource->mFields );
224 attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
225 mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
226 }
227 }
228
229 prepareFields();
230
231 mHasVirtualAttributes = !mFetchJoinInfo.isEmpty() || !mExpressionFieldInfo.isEmpty();
232
233 // by default provider's request is the same
234 mProviderRequest = mRequest;
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
238 if ( mRequest.coordinateTransform().isValid() || mRequest.destinationCrs().isValid() )
239 {
240 mProviderRequest.setCoordinateTransform( QgsCoordinateTransform() );
241 mProviderRequest.setDestinationCrs( QgsCoordinateReferenceSystem(), mRequest.transformContext() );
242 }
243
244 if ( !mDelegatedOrderByToProvider )
245 {
246 mProviderRequest.setOrderBy( QgsFeatureRequest::OrderBy() );
247 }
248
249 if ( !canDelegateLimitToProvider )
250 {
251 mProviderRequest.setLimit( -1 );
252 }
253
254 if ( mProviderRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes )
255 {
256 // prepare list of attributes to match provider fields
257 QSet<int> providerSubset;
258 const QgsAttributeList subset = mProviderRequest.subsetOfAttributes();
259 const int nPendingFields = mSource->mFields.count();
260 for ( const int attrIndex : subset )
261 {
262 if ( attrIndex < 0 || attrIndex >= nPendingFields )
263 continue;
264 if ( mSource->mFields.fieldOrigin( attrIndex ) == Qgis::FieldOrigin::Provider )
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
285 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
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 {
297 mProviderRequest.disableFilter();
298 // can't limit at provider side
299 mProviderRequest.setLimit( -1 );
300 if ( needsGeom )
301 {
302 // have to get geometry from provider in order to evaluate expression on client
303 mProviderRequest.setFlags( mProviderRequest.flags() & ~( static_cast<int>( Qgis::FeatureRequestFlag::NoGeometry ) ) );
304 }
305 break;
306 }
307 }
308 }
309
310 if ( mSource->mHasEditBuffer )
311 {
312 mChangedFeaturesRequest = mProviderRequest;
313 QgsFeatureIds changedIds;
314 QgsChangedAttributesMap::const_iterator attIt = mSource->mChangedAttributeValues.constBegin();
315 for ( ; attIt != mSource->mChangedAttributeValues.constEnd(); ++attIt )
316 {
317 changedIds << attIt.key();
318 }
319 mChangedFeaturesRequest.setFilterFids( changedIds );
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
328 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
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
334 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression || mProviderRequest.spatialFilterType() != Qgis::SpatialFilterType::NoFilter )
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
345 if ( request.filterType() == Qgis::FeatureRequestFilterType::Fid )
346 {
347 mFetchedFid = false;
348 }
349 else // no filter or filter by rect
350 {
351 if ( mSource->mProviderFeatureSource )
352 {
353 if ( mSource->mHasEditBuffer )
354 {
355 mChangedFeaturesIterator = mSource->mProviderFeatureSource->getFeatures( mChangedFeaturesRequest );
356 }
357 else
358 {
359 mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
360 }
361 }
362
363 rewindEditBuffer();
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
487 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
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
528 if ( mProviderIterator.isClosed() )
529 {
531 if ( mSource->mProviderFeatureSource )
532 {
533 mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
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
544 f.setFields( mSource->mFields );
545
546 // update attributes
547 if ( mSource->mHasEditBuffer )
549
552 else
553 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
554
556 {
557 //filtering by expression, and couldn't do it on the provider side
558 mRequest.expressionContext()->setFeature( f );
559 if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
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
589 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
590 {
591 mFetchedFid = false;
592 }
593 else
594 {
595 mProviderIterator.rewind();
597 }
598
599 return true;
600}
601
603{
604 if ( mClosed )
605 return false;
606
607 mProviderIterator.close();
608
610
611 mClosed = true;
612 return true;
613}
614
616{
617 mProviderIterator.setInterruptionChecker( interruptionChecker );
618 mInterruptionChecker = interruptionChecker;
619}
620
622{
623 return mChangedFeaturesIterator.isValid() || mProviderIterator.isValid();
624}
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
651 mFetchAddedFeaturesIt = mSource->mAddedFeatures.constBegin();
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 );
663 f.setFields( mSource->mFields );
664
667 else
668 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
669}
670
671
672
674{
675 // check if changed geometries are in rectangle
676 for ( ; mFetchChangedGeomIt != mSource->mChangedGeometries.constEnd(); mFetchChangedGeomIt++ )
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 {
694 mRequest.expressionContext()->setFeature( f );
695 if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
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{
714 while ( mChangedFeaturesIterator.nextFeature( f ) )
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
737 mRequest.expressionContext()->setFeature( f );
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 );
752 f.setFields( mSource->mFields );
753
755 ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Expression && mRequest.filterExpression()->needsGeometry() ) )
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 {
770 request.setSubsetOfAttributes( mProviderRequest.subsetOfAttributes() );
771 }
772 QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( request );
773 if ( fi.nextFeature( tmp ) )
774 {
775 if ( mHasVirtualAttributes || mSource->mHasEditBuffer )
777 f.setAttributes( tmp.attributes() );
778 }
779 }
780
782}
783
784
785
787{
788 mFetchConsidered = mSource->mDeletedFeatureIds;
789
790 mFetchAddedFeaturesIt = mSource->mAddedFeatures.constEnd();
791 mFetchChangedGeomIt = mSource->mChangedGeometries.constBegin();
792}
793
795{
796 if ( !mSource->mFields.exists( fieldIdx ) )
797 return;
798
799 if ( mSource->mFields.fieldOrigin( fieldIdx ) != Qgis::FieldOrigin::Join )
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 );
816 info.targetField = mSource->mFields.indexFromName( joinInfo->targetFieldName() );
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
824 if ( ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes ) && !mRequest.subsetOfAttributes().contains( info.targetField ) )
825 mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() << info.targetField );
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 auto 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 {
889 mRequest.setFlags( mRequest.flags() & ~( static_cast<int>( Qgis::FeatureRequestFlag::NoGeometry ) ) );
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
904 mFieldsToPrepare = ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes )
905 ? mRequest.subsetOfAttributes()
906 : mSource->mFields.allAttributesList();
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 );
1012 if ( mRequest.invalidGeometryCallback() )
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();
1029 if ( mRequest.invalidGeometryCallback() )
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 request.setRequestMayBeNested( true );
1235 QgsFeatureIterator fi = joinSource->getFeatures( request );
1236
1237 // get first feature
1238 const QList<int> sourceAttrIndexes = attributesSourceToDestLayerMap.keys();
1239 QgsFeature fet;
1240 if ( fi.nextFeature( fet ) )
1241 {
1242 const QgsAttributes attr = fet.attributes();
1243
1244 for ( const int sourceAttrIndex : sourceAttrIndexes )
1245 {
1246 if ( sourceAttrIndex == joinField )
1247 continue;
1248
1249 const int destAttrIndex = attributesSourceToDestLayerMap.value( sourceAttrIndex );
1250
1251 f.setAttribute( destAttrIndex, attr.at( sourceAttrIndex ) );
1252 }
1253 }
1254 else
1255 {
1256 // no suitable join feature found, keeping empty (null) attributes
1257 }
1258}
1259
1260
1261
1262
1264{
1265 const QgsFeatureId featureId = mRequest.filterFid();
1266
1267 // deleted already?
1268 if ( mSource->mDeletedFeatureIds.contains( featureId ) )
1269 return false;
1270
1271 // has changed geometry?
1272 if ( !( mRequest.flags() & Qgis::FeatureRequestFlag::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) )
1273 {
1274 useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f );
1275 return true;
1276 }
1277
1278 // added features
1279 for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter )
1280 {
1281 if ( iter->id() == featureId )
1282 {
1283 useAddedFeature( *iter, f );
1284 return true;
1285 }
1286 }
1287
1288 // regular features
1289 QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
1290 if ( fi.nextFeature( f ) )
1291 {
1292 f.setFields( mSource->mFields );
1293
1294 if ( mSource->mHasEditBuffer )
1296
1299 else
1300 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
1301
1302 return true;
1303 }
1304
1305 return false;
1306}
1307
1309{
1310 QgsAttributes attrs = f.attributes();
1311
1312 // remove all attributes that will disappear - from higher indices to lower
1313 for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
1314 {
1315 attrs.remove( mSource->mDeletedAttributeIds[idx] );
1316 }
1317
1318 // adjust size to accommodate added attributes
1319 attrs.resize( attrs.count() + mSource->mAddedAttributes.count() );
1320
1321 // update changed attributes
1322 if ( mSource->mChangedAttributeValues.contains( f.id() ) )
1323 {
1324 const QgsAttributeMap &map = mSource->mChangedAttributeValues[f.id()];
1325 for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
1326 attrs[it.key()] = it.value();
1327 }
1328 f.setAttributes( attrs );
1329}
1330
1332{
1333 if ( mSource->mChangedGeometries.contains( f.id() ) )
1334 f.setGeometry( mSource->mChangedGeometries[f.id()] );
1335}
1336
1337void QgsVectorLayerFeatureIterator::createExpressionContext()
1338{
1339 mExpressionContext = std::make_unique< QgsExpressionContext >();
1340 mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
1341 mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) ); // skip-keyword-check
1342 mExpressionContext->appendScope( new QgsExpressionContextScope( mSource->mLayerScope ) );
1343 mExpressionContext->setFeedback( mRequest.feedback() );
1344}
1345
1346bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys )
1347{
1348 Q_UNUSED( orderBys )
1349 return mDelegatedOrderByToProvider;
1350}
1351
1352
1353//
1354// QgsVectorLayerSelectedFeatureSource
1355//
1356
1358 : mSource( layer )
1359 , mSelectedFeatureIds( layer->selectedFeatureIds() )
1360 , mWkbType( layer->wkbType() )
1361 , mName( layer->name() )
1362 , mLayer( layer )
1363{}
1364
1366{
1367 QgsFeatureRequest req( request );
1368
1369 // while QgsVectorLayerSelectedFeatureIterator will reject any features not in mSelectedFeatureIds,
1370 // we still tweak the feature request to only request selected feature ids wherever we can -- this
1371 // allows providers to optimise the request and avoid requesting features we don't need
1372 // note that we can't do this for some request types - e.g. expression based requests, so
1373 // in that case we just pass the request on to the provider and let QgsVectorLayerSelectedFeatureIterator
1374 // do ALL the filtering
1375 if ( req.filterFids().isEmpty() && req.filterType() == Qgis::FeatureRequestFilterType::NoFilter )
1376 {
1377 req.setFilterFids( mSelectedFeatureIds );
1378 }
1379 else if ( !req.filterFids().isEmpty() )
1380 {
1381 QgsFeatureIds reqIds = mSelectedFeatureIds;
1382 reqIds.intersect( req.filterFids() );
1383 req.setFilterFids( reqIds );
1384 }
1385
1386 return QgsFeatureIterator( new QgsVectorLayerSelectedFeatureIterator( mSelectedFeatureIds, req, mSource ) );
1387}
1388
1393
1395{
1396 return mSource.fields();
1397}
1398
1400{
1401 return mWkbType;
1402}
1403
1405{
1406 return mSelectedFeatureIds.count();
1407}
1408
1410{
1411 return mName;
1412}
1413
1415{
1416 if ( mLayer )
1417 return mLayer->createExpressionContextScope();
1418 else
1419 return nullptr;
1420}
1421
1423{
1424 if ( mLayer )
1425 return mLayer->hasSpatialIndex();
1426 else
1428}
1429
1430//
1431// QgsVectorLayerSelectedFeatureIterator
1432//
1433
1435QgsVectorLayerSelectedFeatureIterator::QgsVectorLayerSelectedFeatureIterator( const QgsFeatureIds &selectedFeatureIds, const QgsFeatureRequest &request, QgsVectorLayerFeatureSource &source )
1436 : QgsAbstractFeatureIterator( request )
1437 , mSelectedFeatureIds( selectedFeatureIds )
1438{
1439 QgsFeatureRequest sourceRequest = request;
1440 if ( sourceRequest.filterType() == Qgis::FeatureRequestFilterType::Expression && sourceRequest.limit() > 0 )
1441 {
1442 // we can't pass the request limit to the provider here - otherwise the provider will
1443 // limit the number of returned features and may only return a bunch of matching features
1444 // which AREN'T in the selected feature set
1445 sourceRequest.setLimit( -1 );
1446 }
1447 mIterator = source.getFeatures( sourceRequest );
1448}
1449
1450bool QgsVectorLayerSelectedFeatureIterator::rewind()
1451{
1452 return mIterator.rewind();
1453}
1454
1455bool QgsVectorLayerSelectedFeatureIterator::close()
1456{
1457 return mIterator.close();
1458}
1459
1460bool QgsVectorLayerSelectedFeatureIterator::fetchFeature( QgsFeature &f )
1461{
1462 while ( mIterator.nextFeature( f ) )
1463 {
1464 if ( mSelectedFeatureIds.contains( f.id() ) )
1465 return true;
1466 }
1467 return false;
1468}
1469
@ Fid
Filter using feature ID.
Definition qgis.h:2224
@ Expression
Filter using expression.
Definition qgis.h:2225
@ NoFilter
No filter is applied.
Definition qgis.h:2223
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:558
@ Unknown
Spatial index presence cannot be determined, index may or may not exist.
Definition qgis.h:559
@ SubsetOfAttributes
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag).
Definition qgis.h:2197
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2196
@ Critical
Critical/error message.
Definition qgis.h:159
@ NoFilter
No spatial filtering of features.
Definition qgis.h:2252
@ Provider
Field originates from the underlying data provider of the vector layer.
Definition qgis.h:1706
@ Edit
Field has been temporarily added in editing mode.
Definition qgis.h:1708
@ Unknown
The field origin has not been specified.
Definition qgis.h:1705
@ Expression
Field is calculated from an expression.
Definition qgis.h:1709
@ Join
Field originates from a joined layer.
Definition qgis.h:1707
@ NoCheck
No invalid geometry checking.
Definition qgis.h:2239
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Definition qgis.h:2241
@ SkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
Definition qgis.h:2240
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:277
QgsAbstractFeatureIteratorFromSource(QgsVectorLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request)
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.
bool mClosed
Sets to true, as soon as the iterator is closed.
A vector of attributes.
Represents a coordinate reference system (CRS).
Handles coordinate transforms between two coordinate systems.
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.
Handles parsing and evaluation of expressions (formerly called "search strings").
QVariant evaluate()
Evaluate the feature and return the result.
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 rewind()
Resets the iterator to the starting position.
Represents a list of OrderByClauses, with the most important first and the least important last.
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.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the iterator to check if it should be ca...
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
long long limit() const
Returns the maximum number of features to request, or -1 if no limit set.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
Qgis::FeatureRequestFilterType filterType() const
Returns the attribute/ID filter type which is currently set on this request.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
const QgsFeatureIds & filterFids() const
Returns the feature IDs that should be fetched.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets 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
Container of fields for a vector layer.
Definition qgsfields.h:46
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.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:87
QString id
Definition qgsmaplayer.h:83
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
static QgsProject * instance()
Returns the QgsProject singleton instance.
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.
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.
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 dataset.
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:28
QMap< QgsFeatureId, QgsFeature > QgsFeatureMap
Join information prepared for fast attribute id mapping in QgsVectorLayerJoinBuffer::updateFeatureAtt...
QMap< int, int > attributesSourceToDestLayerMap
Mapping from original attribute index to the joined layer index.
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.