QGIS API Documentation 4.3.0-Master (6402a64e93b)
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#include <QString>
34
35using namespace Qt::StringLiterals;
36
37#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
38#include <QThreadStorage>
39#endif
40
41#include <deque>
42#include <memory>
43
45{
46 const QMutexLocker locker( &layer->mFeatureSourceConstructorMutex );
47 const QgsVectorDataProvider *provider = layer->dataProvider();
48 if ( provider )
49 mProviderFeatureSource.reset( provider->featureSource() );
50 mFields = layer->fields();
51 mId = layer->id();
52
53 // update layer's join caches if necessary
54 if ( layer->mJoinBuffer->containsJoins() )
55 layer->mJoinBuffer->createJoinCaches();
56
57 mJoinBuffer.reset( layer->mJoinBuffer->clone() );
58 for ( const QgsVectorLayerJoinInfo &joinInfo : mJoinBuffer->vectorJoins() )
59 {
60 if ( QgsVectorLayer *joinLayer = joinInfo.joinLayer() )
61 {
62 JoinLayerSource source;
63 source.joinSource = std::make_shared< QgsVectorLayerFeatureSource >( joinLayer );
64 source.joinLayerFields = joinLayer->fields();
65 mJoinSources.insert( joinLayer->id(), source );
66 }
67 }
68
69 mExpressionFieldBuffer = std::make_unique<QgsExpressionFieldBuffer>( *layer->mExpressionFieldBuffer );
70 mCrs = layer->crs();
71
72 mHasEditBuffer = layer->editBuffer();
73 if ( mHasEditBuffer )
74 {
75#if 0
76 // TODO[MD]: after merge
77 if ( request.filterType() == QgsFeatureRequest::FilterFid )
78 {
79
80 // only copy relevant parts
81 if ( L->editBuffer()->addedFeatures().contains( request.filterFid() ) )
82 mAddedFeatures.insert( request.filterFid(), L->editBuffer()->addedFeatures()[ request.filterFid()] );
83
84 if ( L->editBuffer()->changedGeometries().contains( request.filterFid() ) )
85 mChangedGeometries.insert( request.filterFid(), L->editBuffer()->changedGeometries()[ request.filterFid()] );
86
87 if ( L->editBuffer()->deletedFeatureIds().contains( request.filterFid() ) )
88 mDeletedFeatureIds.insert( request.filterFid() );
89
90 if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
91 mChangedAttributeValues.insert( request.filterFid(), L->editBuffer()->changedAttributeValues()[ request.filterFid()] );
92
93 if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
94 mChangedFeaturesRequest.setFilterFids( QgsFeatureIds() << request.filterFid() );
95 }
96 else
97 {
98#endif
99 // If we are inside a transaction the iterator "sees" the current status
100 if ( provider && !provider->transaction() )
101 {
106 mAddedAttributes = QList<QgsField>( layer->editBuffer()->addedAttributes() );
108 }
109#if 0
110 }
111#endif
112 }
113
114 const std::unique_ptr< QgsExpressionContextScope > layerScope( QgsExpressionContextUtils::layerScope( layer ) );
115 mLayerScope = *layerScope;
116}
117
119
121{
122 // return feature iterator that does not own this source
123 return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, false, request ) );
124}
125
130
135
137{
138 return mId;
139}
140
141
144{
145 mTransform = mRequest.calculateTransform( mSource->mCrs );
146 mHasValidTransform = mTransform.isValid();
147
148 // prepare spatial filter geometries for optimal speed
149 // since the mDistanceWithin* constraint member variables are all in the DESTINATION CRS,
150 // we set all these upfront before any transformation to the source CRS is done.
151
152 switch ( mRequest.spatialFilterType() )
153 {
154 case Qgis::SpatialFilterType::NoFilter:
155 case Qgis::SpatialFilterType::BoundingBox:
156 break;
157
158 case Qgis::SpatialFilterType::DistanceWithin:
159 if ( !mRequest.referenceGeometry().isEmpty() )
160 {
161 // Note that regardless of whether or not we'll ultimately be able to handoff this check to the underlying provider,
162 // we still need these reference geometry constraints in the vector layer iterator as we need them to check against
163 // the features from the vector layer's edit buffer! (In other words, we cannot completely hand off responsibility for
164 // these checks to the provider and ignore them locally)
165 mDistanceWithinGeom = mRequest.referenceGeometry();
166 mDistanceWithinEngine = mRequest.referenceGeometryEngine();
167 mDistanceWithinEngine->prepareGeometry();
168 mDistanceWithin = mRequest.distanceWithin();
169 }
170 break;
171 }
172
173 bool canDelegateLimitToProvider = true;
174 try
175 {
176 switch ( updateRequestToSourceCrs( mRequest, mTransform ) )
177 {
179 break;
180
182 // we have to disable any limit on the provider's request -- since that request may be returning features which are outside the
183 // distance tolerance, we'll have to fetch them all and then handle the limit check manually only after testing for the distance within constraint
184 canDelegateLimitToProvider = false;
185 break;
186 }
187
188 // mFilterRect is in the source CRS, so we set that now (after request transformation has been done)
189 mFilterRect = mRequest.filterRect();
190 }
191 catch ( QgsCsException & )
192 {
193 // can't reproject request filters
194 close();
195 return;
196 }
197
198 // check whether the order by clause(s) can be delegated to the provider
199 mDelegatedOrderByToProvider = !mSource->mHasEditBuffer;
200 if ( !mRequest.orderBy().isEmpty() )
201 {
202 QSet<int> attributeIndexes;
203 const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields );
204 for ( const int attrIndex : usedAttributeIndices )
205 {
206 if ( mSource->mFields.fieldOrigin( attrIndex ) != Qgis::FieldOrigin::Provider )
207 mDelegatedOrderByToProvider = false;
208
209 attributeIndexes << attrIndex;
210 }
211
212 if ( ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes ) && !mDelegatedOrderByToProvider )
213 {
214 attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
215 mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
216 }
217 }
218
219 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
220 {
221 mRequest.expressionContext()->setFields( mSource->mFields );
222 mRequest.filterExpression()->prepare( mRequest.expressionContext() );
223
224 if ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes )
225 {
226 // ensure that all fields required for filter expressions are prepared
227 QSet<int> attributeIndexes = mRequest.filterExpression()->referencedAttributeIndexes( mSource->mFields );
228 attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
229 mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
230 }
231 }
232
233 prepareFields();
234
235 mHasVirtualAttributes = !mFetchJoinInfo.isEmpty() || !mExpressionFieldInfo.isEmpty();
236
237 // by default provider's request is the same
238 mProviderRequest = mRequest;
239 // but we remove any destination CRS parameter - that is handled in QgsVectorLayerFeatureIterator,
240 // not at the provider level. Otherwise virtual fields depending on geometry would have incorrect
241 // values
242 if ( mRequest.coordinateTransform().isValid() || mRequest.destinationCrs().isValid() )
243 {
244 mProviderRequest.setCoordinateTransform( QgsCoordinateTransform() );
245 mProviderRequest.setDestinationCrs( QgsCoordinateReferenceSystem(), mRequest.transformContext() );
246 }
247
248 if ( !mDelegatedOrderByToProvider )
249 {
250 mProviderRequest.setOrderBy( QgsFeatureRequest::OrderBy() );
251 }
252
253 if ( !canDelegateLimitToProvider )
254 {
255 mProviderRequest.setLimit( -1 );
256 }
257
258 if ( mProviderRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes )
259 {
260 // prepare list of attributes to match provider fields
261 QSet<int> providerSubset;
262 const QgsAttributeList subset = mProviderRequest.subsetOfAttributes();
263 const int nPendingFields = mSource->mFields.count();
264 for ( const int attrIndex : subset )
265 {
266 if ( attrIndex < 0 || attrIndex >= nPendingFields )
267 continue;
268 if ( mSource->mFields.fieldOrigin( attrIndex ) == Qgis::FieldOrigin::Provider )
269 providerSubset << mSource->mFields.fieldOriginIndex( attrIndex );
270 }
271
272 // This is done in order to be prepared to do fallback order bys
273 // and be sure we have the required columns.
274 // TODO:
275 // It would be nicer to first check if we can compile the order by
276 // and only modify the subset if we cannot.
277 if ( !mProviderRequest.orderBy().isEmpty() )
278 {
279 const auto usedAttributeIndices = mProviderRequest.orderBy().usedAttributeIndices( mSource->mFields );
280 for ( const int attrIndex : usedAttributeIndices )
281 {
282 providerSubset << attrIndex;
283 }
284 }
285
286 mProviderRequest.setSubsetOfAttributes( qgis::setToList( providerSubset ) );
287 }
288
289 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
290 {
291 const bool needsGeom = mProviderRequest.filterExpression()->needsGeometry();
292 const auto constReferencedColumns = mProviderRequest.filterExpression()->referencedColumns();
293 for ( const QString &field : constReferencedColumns )
294 {
295 const int idx = source->mFields.lookupField( field );
296
297 // If there are fields in the expression which are not of origin provider, the provider will not be able to filter based on them.
298 // In this case we disable the expression filter.
299 if ( source->mFields.fieldOrigin( idx ) != Qgis::FieldOrigin::Provider )
300 {
301 mProviderRequest.disableFilter();
302 // can't limit at provider side
303 mProviderRequest.setLimit( -1 );
304 if ( needsGeom )
305 {
306 // have to get geometry from provider in order to evaluate expression on client
307 mProviderRequest.setFlags( mProviderRequest.flags() & ~( static_cast<int>( Qgis::FeatureRequestFlag::NoGeometry ) ) );
308 }
309 break;
310 }
311 }
312 }
313
314 if ( mSource->mHasEditBuffer )
315 {
316 mChangedFeaturesRequest = mProviderRequest;
317 QgsFeatureIds changedIds;
318 QgsChangedAttributesMap::const_iterator attIt = mSource->mChangedAttributeValues.constBegin();
319 for ( ; attIt != mSource->mChangedAttributeValues.constEnd(); ++attIt )
320 {
321 changedIds << attIt.key();
322 }
323 mChangedFeaturesRequest.setFilterFids( changedIds );
324
325 if ( mChangedFeaturesRequest.limit() > 0 )
326 {
327 int providerLimit = mProviderRequest.limit();
328
329 // features may be deleted in buffer, so increase limit sent to provider
330 providerLimit += mSource->mDeletedFeatureIds.size();
331
332 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
333 {
334 // attribute changes may mean some features no longer match expression, so increase limit sent to provider
335 providerLimit += mSource->mChangedAttributeValues.size();
336 }
337
338 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression || mProviderRequest.spatialFilterType() != Qgis::SpatialFilterType::NoFilter )
339 {
340 // geometry changes may mean some features no longer match expression or rect, so increase limit sent to provider
341 providerLimit += mSource->mChangedGeometries.size();
342 }
343
344 mProviderRequest.setLimit( providerLimit );
345 mChangedFeaturesRequest.setLimit( providerLimit );
346 }
347 }
348
349 if ( request.filterType() == Qgis::FeatureRequestFilterType::Fid )
350 {
351 mFetchedFid = false;
352 }
353 else // no filter or filter by rect
354 {
355 if ( mSource->mProviderFeatureSource )
356 {
357 if ( mSource->mHasEditBuffer )
358 {
359 mChangedFeaturesIterator = mSource->mProviderFeatureSource->getFeatures( mChangedFeaturesRequest );
360 }
361 else
362 {
363 mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
364 }
365 }
366
367 rewindEditBuffer();
368 }
369}
370
371
378
380
386class QgsThreadStackOverflowGuard
387{
388 public:
389#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
390 QgsThreadStackOverflowGuard( QThreadStorage<std::deque<QString>> &storage, const QString &stackFrameInformation, int maxDepth )
391#else
392 QgsThreadStackOverflowGuard( std::deque<QString> &storage, const QString &stackFrameInformation, int maxDepth )
393#endif
394 : mStorage( storage )
395 , mMaxDepth( maxDepth )
396 {
397#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
398 if ( !storage.hasLocalData() )
399 {
400 storage.setLocalData( std::deque<QString>() );
401 }
402
403 storage.localData().emplace_back( stackFrameInformation );
404#else
405 storage.emplace_back( stackFrameInformation );
406#endif
407 }
408
409 ~QgsThreadStackOverflowGuard()
410 {
411#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
412 mStorage.localData().pop_back();
413#else
414 mStorage.pop_back();
415#endif
416 }
417
418 bool hasStackOverflow() const
419 {
420#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
421 if ( mStorage.localData().size() > mMaxDepth )
422#else
423 if ( mStorage.size() > mMaxDepth )
424#endif
425 return true;
426 else
427 return false;
428 }
429
430 QString topFrames() const
431 {
432 QStringList dumpStack;
433#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
434 const std::deque<QString> &stack = mStorage.localData();
435#else
436 const std::deque<QString> &stack = mStorage;
437#endif
438
439 const int dumpSize = std::min( static_cast<int>( stack.size() ), 10 );
440 auto stackIt = stack.begin();
441 for ( int i = 0; i < dumpSize; ++i, stackIt++ )
442 {
443 dumpStack += *stackIt;
444 }
445
446 return dumpStack.join( '\n' );
447 }
448
449 std::size_t depth() const
450 {
451#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
452 return mStorage.localData().size();
453#else
454 return mStorage.size();
455#endif
456 }
457
458 private:
459#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
460 QThreadStorage<std::deque<QString>> &mStorage;
461#else
462 std::deque<QString> &mStorage;
463#endif
464 std::size_t mMaxDepth;
465};
466
468
470{
471 f.setValid( false );
472
473 if ( mClosed )
474 return false;
475
476#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
477 static QThreadStorage<std::deque<QString>> sStack;
478#else
479 static thread_local std::deque<QString> sStack;
480#endif
481
482 const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
483
484 if ( guard.hasStackOverflow() )
485 {
486 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 );
487 return false;
488 }
489
490 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
491 {
492 if ( mFetchedFid )
493 return false;
494 const bool res = nextFeatureFid( f );
495 if ( res && postProcessFeature( f ) )
496 {
497 mFetchedFid = true;
498 return res;
499 }
500 else
501 {
502 return false;
503 }
504 }
505
506 if ( !mFilterRect.isNull() )
507 {
509 return true;
510
511 // no more changed geometries
512 }
513
515 {
517 return true;
518
520 return true;
521
522 // no more changed features
523 }
524
525 while ( fetchNextAddedFeature( f ) )
526 {
527 return true;
528 }
529 // no more added features
530
531 if ( mProviderIterator.isClosed() )
532 {
534 if ( mSource->mProviderFeatureSource )
535 {
536 mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
537 mProviderIterator.setInterruptionChecker( mInterruptionChecker );
538 }
539 }
540
541 while ( mProviderIterator.nextFeature( f ) )
542 {
543 if ( mFetchConsidered.contains( f.id() ) )
544 continue;
545
546 // TODO[MD]: just one resize of attributes
547 f.setFields( mSource->mFields );
548
549 // update attributes
550 if ( mSource->mHasEditBuffer )
552
555 else
556 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
557
559 {
560 //filtering by expression, and couldn't do it on the provider side
561 mRequest.expressionContext()->setFeature( f );
562 if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
563 {
564 //feature did not match filter
565 continue;
566 }
567 }
568
569 // update geometry
570 // TODO[MK]: FilterRect check after updating the geometry
573
574 if ( !postProcessFeature( f ) )
575 continue;
576
577 return true;
578 }
579 // no more provider features
580
581 close();
582 return false;
583}
584
585
587{
588 if ( mClosed )
589 return false;
590
591 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
592 {
593 mFetchedFid = false;
594 }
595 else
596 {
597 mProviderIterator.rewind();
599 }
600
601 return true;
602}
603
605{
606 if ( mClosed )
607 return false;
608
609 mProviderIterator.close();
610
612
613 mClosed = true;
614 return true;
615}
616
618{
619 mProviderIterator.setInterruptionChecker( interruptionChecker );
620 mInterruptionChecker = interruptionChecker;
621}
622
624{
625 return mChangedFeaturesIterator.isValid() || mProviderIterator.isValid();
626}
627
629{
630 while ( mFetchAddedFeaturesIt != mSource->mAddedFeatures.constBegin() )
631 {
633 const QgsFeatureId fid = mFetchAddedFeaturesIt->id();
634
635 if ( mFetchConsidered.contains( fid ) )
636 // must have changed geometry outside rectangle
637 continue;
638
640
641 // can't test for feature acceptance until after calling useAddedFeature
642 // since acceptFeature may rely on virtual fields
643 if ( !mRequest.acceptFeature( f ) )
644 // skip features which are not accepted by the filter
645 continue;
646
647 if ( !postProcessFeature( f ) )
648 continue;
649
650 return true;
651 }
652
653 mFetchAddedFeaturesIt = mSource->mAddedFeatures.constBegin();
654 return false; // no more added features
655}
656
657
659{
660 // since QgsFeature is implicitly shared, it's more efficient to just copy the
661 // whole feature, even if flags like NoGeometry or a subset of attributes is set at the request.
662 // This helps potentially avoid an unnecessary detach of the feature
663 f = src;
664 f.setValid( true );
665 f.setFields( mSource->mFields );
666
669 else
670 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
671}
672
673
675{
676 // check if changed geometries are in rectangle
677 for ( ; mFetchChangedGeomIt != mSource->mChangedGeometries.constEnd(); mFetchChangedGeomIt++ )
678 {
679 const QgsFeatureId fid = mFetchChangedGeomIt.key();
680
681 if ( mFetchConsidered.contains( fid ) )
682 // skip deleted features
683 continue;
684
685 mFetchConsidered << fid;
686
687 if ( !mFilterRect.isNull() && !mFetchChangedGeomIt->intersects( mFilterRect ) )
688 // skip changed geometries not in rectangle and don't check again
689 continue;
690
692
694 {
695 mRequest.expressionContext()->setFeature( f );
696 if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
697 {
698 continue;
699 }
700 }
701
702 if ( postProcessFeature( f ) )
703 {
704 // return complete feature
706 return true;
707 }
708 }
709
710 return false; // no more changed geometries
711}
712
714{
715 while ( mChangedFeaturesIterator.nextFeature( f ) )
716 {
717 if ( mFetchConsidered.contains( f.id() ) )
718 continue;
719
720 mFetchConsidered << f.id();
721
723
724 // also update geometry if needed
725 const auto changedGeometryIt = mSource->mChangedGeometries.constFind( f.id() );
726 if ( changedGeometryIt != mSource->mChangedGeometries.constEnd() )
727 {
728 if ( !mFilterRect.isNull() && !changedGeometryIt->intersects( mFilterRect ) )
729 // skip changed geometries not in rectangle and don't check again
730 continue;
731
732 f.setGeometry( *changedGeometryIt );
733 }
734
737
738 mRequest.expressionContext()->setFeature( f );
739 if ( mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() && postProcessFeature( f ) )
740 {
741 return true;
742 }
743 }
744
745 return false;
746}
747
748
750{
751 f.setId( fid );
752 f.setValid( true );
753 f.setFields( mSource->mFields );
754
755 if ( !( mRequest.flags() & Qgis::FeatureRequestFlag::NoGeometry ) || ( 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
786{
787 mFetchConsidered = mSource->mDeletedFeatureIds;
788
789 mFetchAddedFeaturesIt = mSource->mAddedFeatures.constEnd();
790 mFetchChangedGeomIt = mSource->mChangedGeometries.constBegin();
791}
792
794{
795 if ( !mSource->mFields.exists( fieldIdx ) )
796 return;
797
798 if ( mSource->mFields.fieldOrigin( fieldIdx ) != Qgis::FieldOrigin::Join )
799 return;
800
801 int sourceLayerIndex;
802 const QgsVectorLayerJoinInfo *joinInfo = mSource->mJoinBuffer->joinForFieldIndex( fieldIdx, mSource->mFields, sourceLayerIndex );
803 Q_ASSERT( joinInfo );
804
805 auto joinSourceIt = mSource->mJoinSources.constFind( joinInfo->joinLayerId() );
806 if ( joinSourceIt == mSource->mJoinSources.constEnd() )
807 return; // invalid join (unresolved reference to layer)
808
809 if ( !mFetchJoinInfo.contains( joinInfo ) )
810 {
811 FetchJoinInfo info;
812 info.joinInfo = joinInfo;
813 info.joinSource = joinSourceIt->joinSource;
814 info.indexOffset = mSource->mJoinBuffer->joinedFieldsOffset( joinInfo, mSource->mFields );
815 info.targetField = mSource->mFields.indexFromName( joinInfo->targetFieldName() );
816 info.joinField = joinSourceIt->joinLayerFields.indexFromName( joinInfo->joinFieldName() );
817 info.joinLayerFields = joinSourceIt->joinLayerFields;
818
819 // for joined fields, we always need to request the targetField from the provider too
820 if ( !mPreparedFields.contains( info.targetField ) && !mFieldsToPrepare.contains( info.targetField ) )
821 mFieldsToPrepare << info.targetField;
822
823 if ( ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes ) && !mRequest.subsetOfAttributes().contains( info.targetField ) )
824 mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() << info.targetField );
825
826 mFetchJoinInfo.insert( joinInfo, info );
827 }
828
829 // store field source index - we'll need it when fetching from provider
830 mFetchJoinInfo[joinInfo].attributes.push_back( sourceLayerIndex );
831 mFetchJoinInfo[joinInfo].attributesSourceToDestLayerMap[sourceLayerIndex] = fieldIdx;
832}
833
834
836{
837#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
838 static QThreadStorage<std::deque<QString>> sStack;
839#else
840 static thread_local std::deque<QString> sStack;
841#endif
842
843 const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
844
845 if ( guard.hasStackOverflow() )
846 {
848 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 ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList();
905
906 while ( !mFieldsToPrepare.isEmpty() )
907 {
908 const int fieldIdx = mFieldsToPrepare.takeFirst();
909 if ( mPreparedFields.contains( fieldIdx ) )
910 continue;
911
912 mPreparedFields << fieldIdx;
913 prepareField( fieldIdx );
914 }
915
916 //sort joins by dependency
917 if ( !mFetchJoinInfo.empty() )
918 {
919 createOrderedJoinList();
920 }
921}
922
923void QgsVectorLayerFeatureIterator::createOrderedJoinList()
924{
925 mOrderedJoinInfoList = mFetchJoinInfo.values();
926 if ( mOrderedJoinInfoList.size() < 2 )
927 {
928 return;
929 }
930
931 QSet<int> resolvedFields; //todo: get provider / virtual fields without joins
932
933 //add all provider fields without joins as resolved fields
934 QList< int >::const_iterator prepFieldIt = mPreparedFields.constBegin();
935 for ( ; prepFieldIt != mPreparedFields.constEnd(); ++prepFieldIt )
936 {
937 if ( mSource->mFields.fieldOrigin( *prepFieldIt ) != Qgis::FieldOrigin::Join )
938 {
939 resolvedFields.insert( *prepFieldIt );
940 }
941 }
942
943 //iterate through the joins. If target field is not yet covered, move the entry to the end of the list
944
945 //some join combinations might not have a resolution at all
946 const int maxIterations = ( mOrderedJoinInfoList.size() + 1 ) * mOrderedJoinInfoList.size() / 2.0;
947 int currentIteration = 0;
948
949 for ( int i = 0; i < mOrderedJoinInfoList.size() - 1; ++i )
950 {
951 if ( !resolvedFields.contains( mOrderedJoinInfoList.at( i ).targetField ) )
952 {
953 mOrderedJoinInfoList.append( mOrderedJoinInfoList.at( i ) );
954 mOrderedJoinInfoList.removeAt( i );
955 --i;
956 }
957 else
958 {
959 const int offset = mOrderedJoinInfoList.at( i ).indexOffset;
960 const int joinField = mOrderedJoinInfoList.at( i ).joinField;
961
962 const QgsAttributeList attributes = mOrderedJoinInfoList.at( i ).attributes;
963 for ( int n = 0; n < attributes.size(); n++ )
964 {
965 if ( n != joinField )
966 {
967 resolvedFields.insert( joinField < n ? n + offset - 1 : n + offset );
968 }
969 }
970 }
971
972 ++currentIteration;
973 if ( currentIteration >= maxIterations )
974 {
975 break;
976 }
977 }
978}
979
980bool QgsVectorLayerFeatureIterator::postProcessFeature( QgsFeature &feature )
981{
982 bool result = checkGeometryValidity( feature );
983 if ( result && mHasValidTransform )
985
986 if ( result && mDistanceWithinEngine && feature.hasGeometry() )
987 {
988 result = mDistanceWithinEngine->distanceWithin( feature.geometry().constGet(), mDistanceWithin );
989 }
990
991 return result;
992}
993
994bool QgsVectorLayerFeatureIterator::checkGeometryValidity( const QgsFeature &feature )
995{
996 switch ( mRequest.invalidGeometryCheck() )
997 {
999 return true;
1000
1002 {
1003 if ( !feature.hasGeometry() )
1004 return true;
1005
1006 if ( !feature.geometry().isGeosValid() )
1007 {
1008 QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
1009 if ( mRequest.invalidGeometryCallback() )
1010 {
1011 mRequest.invalidGeometryCallback()( feature );
1012 }
1013 return false;
1014 }
1015 break;
1016 }
1017
1019 if ( !feature.hasGeometry() )
1020 return true;
1021
1022 if ( !feature.geometry().isGeosValid() )
1023 {
1024 QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
1025 close();
1026 if ( mRequest.invalidGeometryCallback() )
1027 {
1028 mRequest.invalidGeometryCallback()( feature );
1029 }
1030 return false;
1031 }
1032 break;
1033 }
1034
1035 return true;
1036}
1037
1039{
1040 switch ( mSource->mFields.fieldOrigin( fieldIdx ) )
1041 {
1043 prepareExpression( fieldIdx );
1044 break;
1045
1047 if ( mSource->mJoinBuffer->containsJoins() )
1048 {
1049 prepareJoin( fieldIdx );
1050 }
1051 break;
1052
1056 break;
1057 }
1058}
1059
1061{
1062 QList< FetchJoinInfo >::const_iterator joinIt = mOrderedJoinInfoList.constBegin();
1063 for ( ; joinIt != mOrderedJoinInfoList.constEnd(); ++joinIt )
1064 {
1065 const QVariant targetFieldValue = f.attribute( joinIt->targetField );
1066 if ( !targetFieldValue.isValid() )
1067 continue;
1068
1069 const QHash< QString, QgsAttributes> &memoryCache = joinIt->joinInfo->cachedAttributes;
1070 if ( memoryCache.isEmpty() )
1071 {
1072 if ( joinIt->joinInfo->isUsingMemoryCache() && !joinIt->joinInfo->cacheDirty )
1073 continue;
1074
1075 joinIt->addJoinedAttributesDirect( f, targetFieldValue );
1076 }
1077 else
1078 joinIt->addJoinedAttributesCached( f, targetFieldValue );
1079 }
1080}
1081
1083{
1084 // make sure we have space for newly added attributes
1085 QgsAttributes attr = f.attributes();
1086 attr.resize( mSource->mFields.count() ); // Provider attrs count + joined attrs count + expression attrs count
1087 f.setAttributes( attr );
1088
1089 // possible TODO - handle combinations of expression -> join -> expression -> join?
1090 // but for now, write that off as too complex and an unlikely rare, unsupported use case
1091
1092 QList< int > fetchedVirtualAttributes;
1093 //first, check through joins for any virtual fields we need
1094 QMap<const QgsVectorLayerJoinInfo *, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
1095 for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
1096 {
1097 if ( mExpressionFieldInfo.contains( joinIt->targetField ) )
1098 {
1099 // have to calculate expression field before we can handle this join
1100 addExpressionAttribute( f, joinIt->targetField );
1101 fetchedVirtualAttributes << joinIt->targetField;
1102 }
1103 }
1104
1105 if ( !mFetchJoinInfo.isEmpty() )
1107
1108 // add remaining expression fields
1109 if ( !mExpressionFieldInfo.isEmpty() )
1110 {
1111 QMap<int, QgsExpression *>::ConstIterator it = mExpressionFieldInfo.constBegin();
1112 for ( ; it != mExpressionFieldInfo.constEnd(); ++it )
1113 {
1114 if ( fetchedVirtualAttributes.contains( it.key() ) )
1115 continue;
1116
1117 addExpressionAttribute( f, it.key() );
1118 }
1119 }
1120}
1121
1123{
1124 QgsExpression *exp = mExpressionFieldInfo.value( attrIndex );
1125 if ( exp )
1126 {
1127 if ( !mExpressionContext )
1128 createExpressionContext();
1129
1130 mExpressionContext->setFeature( f );
1131 QVariant val = exp->evaluate( mExpressionContext.get() );
1132 ( void ) mSource->mFields.at( attrIndex ).convertCompatible( val );
1133 f.setAttribute( attrIndex, val );
1134 }
1135 else
1136 {
1137 f.setAttribute( attrIndex, QVariant() );
1138 }
1139}
1140
1142{
1143 Q_UNUSED( simplifyMethod )
1144 return false;
1145}
1146
1147bool QgsVectorLayerFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
1148{
1149 Q_UNUSED( methodType )
1150 return false;
1151}
1152
1153
1155{
1156 const QHash<QString, QgsAttributes> &memoryCache = joinInfo->cachedAttributes;
1157 const QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
1158 if ( it == memoryCache.constEnd() )
1159 return; // joined value not found -> leaving the attributes empty (null)
1160
1161 int index = indexOffset;
1162
1163 const QgsAttributes &featureAttributes = it.value();
1164 for ( int i = 0; i < featureAttributes.count(); ++i )
1165 {
1166 f.setAttribute( index++, featureAttributes.at( i ) );
1167 }
1168}
1169
1170
1172{
1173#if 0 // this is not thread safe -- we cannot access the layer here as this will be called from non-main threads.
1174 // Shortcut
1175 if ( joinLayer && ! joinLayer->hasFeatures() )
1176 {
1177 return;
1178 }
1179#endif
1180
1181 // no memory cache, query the joined values by setting substring
1182 QString subsetString;
1183
1184 const QString joinFieldName = joinInfo->joinFieldName();
1185
1186 subsetString.append( u"\"%1\""_s.arg( joinFieldName ) );
1187
1188 if ( QgsVariantUtils::isNull( joinValue ) )
1189 {
1190 subsetString += " IS NULL"_L1;
1191 }
1192 else
1193 {
1194 QString v = joinValue.toString();
1195 switch ( joinValue.userType() )
1196 {
1197 case QMetaType::Type::Int:
1198 case QMetaType::Type::LongLong:
1199 case QMetaType::Type::Double:
1200 break;
1201
1202 default:
1203 case QMetaType::Type::QString:
1204 v.replace( '\'', "''"_L1 );
1205 v.prepend( '\'' ).append( '\'' );
1206 break;
1207 }
1208 subsetString += '=' + v;
1209 }
1210
1211 QList<int> joinedAttributeIndices;
1212
1213 // maybe user requested just a subset of layer's attributes
1214 // so we do not have to cache everything
1215 if ( joinInfo->hasSubset() )
1216 {
1217 const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinInfo, joinLayerFields );
1218 const QVector<int> subsetIndices = QgsVectorLayerJoinBuffer::joinSubsetIndices( joinLayerFields, subsetNames );
1219 joinedAttributeIndices = qgis::setToList( qgis::listToSet( attributes ).intersect( qgis::listToSet( subsetIndices.toList() ) ) );
1220 }
1221 else
1222 {
1223 joinedAttributeIndices = attributes;
1224 }
1225
1226 // we don't need the join field, it is already present in the other table
1227 joinedAttributeIndices.removeAll( joinField );
1228
1229 // select (no geometry)
1230 QgsFeatureRequest request;
1232 request.setSubsetOfAttributes( joinedAttributeIndices );
1233 request.setFilterExpression( subsetString );
1234 request.setLimit( 1 );
1235 request.setRequestMayBeNested( true );
1236 QgsFeatureIterator fi = joinSource->getFeatures( request );
1237
1238 // get first feature
1239 const QList<int> sourceAttrIndexes = attributesSourceToDestLayerMap.keys();
1240 QgsFeature fet;
1241 if ( fi.nextFeature( fet ) )
1242 {
1243 const QgsAttributes attr = fet.attributes();
1244
1245 for ( const int sourceAttrIndex : sourceAttrIndexes )
1246 {
1247 if ( sourceAttrIndex == joinField )
1248 continue;
1249
1250 const int destAttrIndex = attributesSourceToDestLayerMap.value( sourceAttrIndex );
1251
1252 f.setAttribute( destAttrIndex, attr.at( sourceAttrIndex ) );
1253 }
1254 }
1255 else
1256 {
1257 // no suitable join feature found, keeping empty (null) attributes
1258 }
1259}
1260
1261
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
1288 QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
1289 if ( fi.nextFeature( f ) )
1290 {
1291 f.setFields( mSource->mFields );
1292
1293 if ( mSource->mHasEditBuffer )
1295
1298 else
1299 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
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 {
1323 const QgsAttributeMap &map = mSource->mChangedAttributeValues[f.id()];
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
1331{
1332 if ( mSource->mChangedGeometries.contains( f.id() ) )
1333 f.setGeometry( mSource->mChangedGeometries[f.id()] );
1334}
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.
Definition qgis.h:2389
@ Expression
Filter using expression.
Definition qgis.h:2390
@ NoFilter
No filter is applied.
Definition qgis.h:2388
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:599
@ Unknown
Spatial index presence cannot be determined, index may or may not exist.
Definition qgis.h:600
@ SubsetOfAttributes
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag).
Definition qgis.h:2361
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2360
@ Critical
Critical/error message.
Definition qgis.h:163
@ NoFilter
No spatial filtering of features.
Definition qgis.h:2417
@ Provider
Field originates from the underlying data provider of the vector layer.
Definition qgis.h:1856
@ Edit
Field has been temporarily added in editing mode.
Definition qgis.h:1858
@ Unknown
The field origin has not been specified.
Definition qgis.h:1855
@ Expression
Field is calculated from an expression.
Definition qgis.h:1859
@ Join
Field originates from a joined layer.
Definition qgis.h:1857
@ NoCheck
No invalid geometry checking.
Definition qgis.h:2404
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Definition qgis.h:2406
@ SkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
Definition qgis.h:2405
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
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:60
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
QgsAttributes attributes
Definition qgsfeature.h:64
QgsFeatureId id
Definition qgsfeature.h:63
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:66
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:45
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:90
QString id
Definition qgsmaplayer.h:86
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(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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:30
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.