QGIS API Documentation 3.99.0-Master (d270888f95f)
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
390#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
391 QgsThreadStackOverflowGuard( QThreadStorage<std::deque<QString>> &storage, const QString &stackFrameInformation, int maxDepth )
392#else
393 QgsThreadStackOverflowGuard( std::deque<QString> &storage, const QString &stackFrameInformation, int maxDepth )
394#endif
395 : mStorage( storage )
396 , mMaxDepth( maxDepth )
397 {
398#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
399 if ( !storage.hasLocalData() )
400 {
401 storage.setLocalData( std::deque<QString>() );
402 }
403
404 storage.localData().emplace_back( stackFrameInformation );
405#else
406 storage.emplace_back( stackFrameInformation );
407#endif
408 }
409
410 ~QgsThreadStackOverflowGuard()
411 {
412#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
413 mStorage.localData().pop_back();
414#else
415 mStorage.pop_back();
416#endif
417 }
418
419 bool hasStackOverflow() const
420 {
421#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
422 if ( mStorage.localData().size() > mMaxDepth )
423#else
424 if ( mStorage.size() > mMaxDepth )
425#endif
426 return true;
427 else
428 return false;
429 }
430
431 QString topFrames() const
432 {
433 QStringList dumpStack;
434#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
435 const std::deque<QString> &stack = mStorage.localData();
436#else
437 const std::deque<QString> &stack = mStorage;
438#endif
439
440 const int dumpSize = std::min( static_cast<int>( stack.size() ), 10 );
441 auto stackIt = stack.begin();
442 for ( int i = 0; i < dumpSize; ++i, stackIt++ )
443 {
444 dumpStack += *stackIt;
445 }
446
447 return dumpStack.join( '\n' );
448 }
449
450 std::size_t depth() const
451 {
452#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
453 return mStorage.localData().size();
454#else
455 return mStorage.size();
456#endif
457 }
458
459 private:
460#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
461 QThreadStorage<std::deque<QString>> &mStorage;
462#else
463 std::deque<QString> &mStorage;
464#endif
465 std::size_t mMaxDepth;
466};
467
469
471{
472 f.setValid( false );
473
474 if ( mClosed )
475 return false;
476
477#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
478 static QThreadStorage<std::deque<QString>> sStack;
479#else
480 static thread_local std::deque<QString> sStack;
481#endif
482
483 const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
484
485 if ( guard.hasStackOverflow() )
486 {
487 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 );
488 return false;
489 }
490
491 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
492 {
493 if ( mFetchedFid )
494 return false;
495 const bool res = nextFeatureFid( f );
496 if ( res && postProcessFeature( f ) )
497 {
498 mFetchedFid = true;
499 return res;
500 }
501 else
502 {
503 return false;
504 }
505 }
506
507 if ( !mFilterRect.isNull() )
508 {
510 return true;
511
512 // no more changed geometries
513 }
514
516 {
518 return true;
519
521 return true;
522
523 // no more changed features
524 }
525
526 while ( fetchNextAddedFeature( f ) )
527 {
528 return true;
529 }
530 // no more added features
531
532 if ( mProviderIterator.isClosed() )
533 {
535 if ( mSource->mProviderFeatureSource )
536 {
537 mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
538 mProviderIterator.setInterruptionChecker( mInterruptionChecker );
539 }
540 }
541
542 while ( mProviderIterator.nextFeature( f ) )
543 {
544 if ( mFetchConsidered.contains( f.id() ) )
545 continue;
546
547 // TODO[MD]: just one resize of attributes
548 f.setFields( mSource->mFields );
549
550 // update attributes
551 if ( mSource->mHasEditBuffer )
553
556 else
557 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
558
560 {
561 //filtering by expression, and couldn't do it on the provider side
562 mRequest.expressionContext()->setFeature( f );
563 if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
564 {
565 //feature did not match filter
566 continue;
567 }
568 }
569
570 // update geometry
571 // TODO[MK]: FilterRect check after updating the geometry
574
575 if ( !postProcessFeature( f ) )
576 continue;
577
578 return true;
579 }
580 // no more provider features
581
582 close();
583 return false;
584}
585
586
587
589{
590 if ( mClosed )
591 return false;
592
593 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
594 {
595 mFetchedFid = false;
596 }
597 else
598 {
599 mProviderIterator.rewind();
601 }
602
603 return true;
604}
605
607{
608 if ( mClosed )
609 return false;
610
611 mProviderIterator.close();
612
614
615 mClosed = true;
616 return true;
617}
618
620{
621 mProviderIterator.setInterruptionChecker( interruptionChecker );
622 mInterruptionChecker = interruptionChecker;
623}
624
626{
627 return mChangedFeaturesIterator.isValid() || mProviderIterator.isValid();
628}
629
631{
632 while ( mFetchAddedFeaturesIt != mSource->mAddedFeatures.constBegin() )
633 {
635 const QgsFeatureId fid = mFetchAddedFeaturesIt->id();
636
637 if ( mFetchConsidered.contains( fid ) )
638 // must have changed geometry outside rectangle
639 continue;
640
642
643 // can't test for feature acceptance until after calling useAddedFeature
644 // since acceptFeature may rely on virtual fields
645 if ( !mRequest.acceptFeature( f ) )
646 // skip features which are not accepted by the filter
647 continue;
648
649 if ( !postProcessFeature( f ) )
650 continue;
651
652 return true;
653 }
654
655 mFetchAddedFeaturesIt = mSource->mAddedFeatures.constBegin();
656 return false; // no more added features
657}
658
659
661{
662 // since QgsFeature is implicitly shared, it's more efficient to just copy the
663 // whole feature, even if flags like NoGeometry or a subset of attributes is set at the request.
664 // This helps potentially avoid an unnecessary detach of the feature
665 f = src;
666 f.setValid( true );
667 f.setFields( mSource->mFields );
668
671 else
672 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
673}
674
675
676
678{
679 // check if changed geometries are in rectangle
680 for ( ; mFetchChangedGeomIt != mSource->mChangedGeometries.constEnd(); mFetchChangedGeomIt++ )
681 {
682 const QgsFeatureId fid = mFetchChangedGeomIt.key();
683
684 if ( mFetchConsidered.contains( fid ) )
685 // skip deleted features
686 continue;
687
688 mFetchConsidered << fid;
689
690 if ( !mFilterRect.isNull() && !mFetchChangedGeomIt->intersects( mFilterRect ) )
691 // skip changed geometries not in rectangle and don't check again
692 continue;
693
695
697 {
698 mRequest.expressionContext()->setFeature( f );
699 if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
700 {
701 continue;
702 }
703 }
704
705 if ( postProcessFeature( f ) )
706 {
707 // return complete feature
709 return true;
710 }
711 }
712
713 return false; // no more changed geometries
714}
715
717{
718 while ( mChangedFeaturesIterator.nextFeature( f ) )
719 {
720 if ( mFetchConsidered.contains( f.id() ) )
721 continue;
722
723 mFetchConsidered << f.id();
724
726
727 // also update geometry if needed
728 const auto changedGeometryIt = mSource->mChangedGeometries.constFind( f.id() );
729 if ( changedGeometryIt != mSource->mChangedGeometries.constEnd() )
730 {
731 if ( !mFilterRect.isNull() && !changedGeometryIt->intersects( mFilterRect ) )
732 // skip changed geometries not in rectangle and don't check again
733 continue;
734
735 f.setGeometry( *changedGeometryIt );
736 }
737
740
741 mRequest.expressionContext()->setFeature( f );
742 if ( mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() && postProcessFeature( f ) )
743 {
744 return true;
745 }
746 }
747
748 return false;
749}
750
751
753{
754 f.setId( fid );
755 f.setValid( true );
756 f.setFields( mSource->mFields );
757
759 ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Expression && mRequest.filterExpression()->needsGeometry() ) )
760 {
761 f.setGeometry( geom );
762 }
763
764 const bool subsetAttrs = ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes );
765 if ( !subsetAttrs || !mRequest.subsetOfAttributes().isEmpty() )
766 {
767 // retrieve attributes from provider
768 QgsFeature tmp;
769 //mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes );
770 QgsFeatureRequest request;
772 if ( subsetAttrs )
773 {
774 request.setSubsetOfAttributes( mProviderRequest.subsetOfAttributes() );
775 }
776 QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( request );
777 if ( fi.nextFeature( tmp ) )
778 {
779 if ( mHasVirtualAttributes || mSource->mHasEditBuffer )
781 f.setAttributes( tmp.attributes() );
782 }
783 }
784
786}
787
788
789
791{
792 mFetchConsidered = mSource->mDeletedFeatureIds;
793
794 mFetchAddedFeaturesIt = mSource->mAddedFeatures.constEnd();
795 mFetchChangedGeomIt = mSource->mChangedGeometries.constBegin();
796}
797
799{
800 if ( !mSource->mFields.exists( fieldIdx ) )
801 return;
802
803 if ( mSource->mFields.fieldOrigin( fieldIdx ) != Qgis::FieldOrigin::Join )
804 return;
805
806 int sourceLayerIndex;
807 const QgsVectorLayerJoinInfo *joinInfo = mSource->mJoinBuffer->joinForFieldIndex( fieldIdx, mSource->mFields, sourceLayerIndex );
808 Q_ASSERT( joinInfo );
809
810 auto joinSourceIt = mSource->mJoinSources.constFind( joinInfo->joinLayerId() );
811 if ( joinSourceIt == mSource->mJoinSources.constEnd() )
812 return; // invalid join (unresolved reference to layer)
813
814 if ( !mFetchJoinInfo.contains( joinInfo ) )
815 {
816 FetchJoinInfo info;
817 info.joinInfo = joinInfo;
818 info.joinSource = joinSourceIt->joinSource;
819 info.indexOffset = mSource->mJoinBuffer->joinedFieldsOffset( joinInfo, mSource->mFields );
820 info.targetField = mSource->mFields.indexFromName( joinInfo->targetFieldName() );
821 info.joinField = joinSourceIt->joinLayerFields.indexFromName( joinInfo->joinFieldName() );
822 info.joinLayerFields = joinSourceIt->joinLayerFields;
823
824 // for joined fields, we always need to request the targetField from the provider too
825 if ( !mPreparedFields.contains( info.targetField ) && !mFieldsToPrepare.contains( info.targetField ) )
826 mFieldsToPrepare << info.targetField;
827
828 if ( ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes ) && !mRequest.subsetOfAttributes().contains( info.targetField ) )
829 mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() << info.targetField );
830
831 mFetchJoinInfo.insert( joinInfo, info );
832 }
833
834 // store field source index - we'll need it when fetching from provider
835 mFetchJoinInfo[ joinInfo ].attributes.push_back( sourceLayerIndex );
836 mFetchJoinInfo[ joinInfo ].attributesSourceToDestLayerMap[sourceLayerIndex] = fieldIdx;
837}
838
839
841{
842#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
843 static QThreadStorage<std::deque<QString>> sStack;
844#else
845 static thread_local std::deque<QString> sStack;
846#endif
847
848 const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
849
850 if ( guard.hasStackOverflow() )
851 {
852 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 );
853 return;
854 }
855
856 const QList<QgsExpressionFieldBuffer::ExpressionField> &exps = mSource->mExpressionFieldBuffer->expressions();
857
858 const int oi = mSource->mFields.fieldOriginIndex( fieldIdx );
859 auto exp = std::make_unique<QgsExpression>( exps[oi].cachedExpression );
860
862 da.setSourceCrs( mSource->mCrs, QgsProject::instance()->transformContext() ); // skip-keyword-check
863 da.setEllipsoid( QgsProject::instance()->ellipsoid() ); // skip-keyword-check
864 exp->setGeomCalculator( &da );
865 exp->setDistanceUnits( QgsProject::instance()->distanceUnits() ); // skip-keyword-check
866 exp->setAreaUnits( QgsProject::instance()->areaUnits() ); // skip-keyword-check
867
868 if ( !mExpressionContext )
869 createExpressionContext();
870 exp->prepare( mExpressionContext.get() );
871 const QSet<int> referencedColumns = exp->referencedAttributeIndexes( mSource->fields() );
872
873 QSet<int> requestedAttributes = qgis::listToSet( mRequest.subsetOfAttributes() );
874
875 for ( const int dependentFieldIdx : referencedColumns )
876 {
878 {
879 requestedAttributes += dependentFieldIdx;
880 }
881 // also need to fetch this dependent field
882 if ( !mPreparedFields.contains( dependentFieldIdx ) && !mFieldsToPrepare.contains( dependentFieldIdx ) )
883 mFieldsToPrepare << dependentFieldIdx;
884 }
885
887 {
888 mRequest.setSubsetOfAttributes( qgis::setToList( requestedAttributes ) );
889 }
890
891 if ( exp->needsGeometry() )
892 {
893 mRequest.setFlags( mRequest.flags() & ~( static_cast<int>( Qgis::FeatureRequestFlag::NoGeometry ) ) );
894 }
895
896 mExpressionFieldInfo.insert( fieldIdx, exp.release() );
897}
898
900{
901 mPreparedFields.clear();
902 mFieldsToPrepare.clear();
903 mFetchJoinInfo.clear();
904 mOrderedJoinInfoList.clear();
905
906 mExpressionContext.reset();
907
908 mFieldsToPrepare = ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes )
909 ? mRequest.subsetOfAttributes()
910 : mSource->mFields.allAttributesList();
911
912 while ( !mFieldsToPrepare.isEmpty() )
913 {
914 const int fieldIdx = mFieldsToPrepare.takeFirst();
915 if ( mPreparedFields.contains( fieldIdx ) )
916 continue;
917
918 mPreparedFields << fieldIdx;
919 prepareField( fieldIdx );
920 }
921
922 //sort joins by dependency
923 if ( !mFetchJoinInfo.empty() )
924 {
925 createOrderedJoinList();
926 }
927
928}
929
930void QgsVectorLayerFeatureIterator::createOrderedJoinList()
931{
932 mOrderedJoinInfoList = mFetchJoinInfo.values();
933 if ( mOrderedJoinInfoList.size() < 2 )
934 {
935 return;
936 }
937
938 QSet<int> resolvedFields; //todo: get provider / virtual fields without joins
939
940 //add all provider fields without joins as resolved fields
941 QList< int >::const_iterator prepFieldIt = mPreparedFields.constBegin();
942 for ( ; prepFieldIt != mPreparedFields.constEnd(); ++prepFieldIt )
943 {
944 if ( mSource->mFields.fieldOrigin( *prepFieldIt ) != Qgis::FieldOrigin::Join )
945 {
946 resolvedFields.insert( *prepFieldIt );
947 }
948 }
949
950 //iterate through the joins. If target field is not yet covered, move the entry to the end of the list
951
952 //some join combinations might not have a resolution at all
953 const int maxIterations = ( mOrderedJoinInfoList.size() + 1 ) * mOrderedJoinInfoList.size() / 2.0;
954 int currentIteration = 0;
955
956 for ( int i = 0; i < mOrderedJoinInfoList.size() - 1; ++i )
957 {
958 if ( !resolvedFields.contains( mOrderedJoinInfoList.at( i ).targetField ) )
959 {
960 mOrderedJoinInfoList.append( mOrderedJoinInfoList.at( i ) );
961 mOrderedJoinInfoList.removeAt( i );
962 --i;
963 }
964 else
965 {
966 const int offset = mOrderedJoinInfoList.at( i ).indexOffset;
967 const int joinField = mOrderedJoinInfoList.at( i ).joinField;
968
969 const QgsAttributeList attributes = mOrderedJoinInfoList.at( i ).attributes;
970 for ( int n = 0; n < attributes.size(); n++ )
971 {
972 if ( n != joinField )
973 {
974 resolvedFields.insert( joinField < n ? n + offset - 1 : n + offset );
975 }
976 }
977 }
978
979 ++currentIteration;
980 if ( currentIteration >= maxIterations )
981 {
982 break;
983 }
984 }
985}
986
987bool QgsVectorLayerFeatureIterator::postProcessFeature( QgsFeature &feature )
988{
989 bool result = checkGeometryValidity( feature );
990 if ( result && mHasValidTransform )
992
993 if ( result && mDistanceWithinEngine && feature.hasGeometry() )
994 {
995 result = mDistanceWithinEngine->distanceWithin( feature.geometry().constGet(), mDistanceWithin );
996 }
997
998 return result;
999}
1000
1001bool QgsVectorLayerFeatureIterator::checkGeometryValidity( const QgsFeature &feature )
1002{
1003 switch ( mRequest.invalidGeometryCheck() )
1004 {
1006 return true;
1007
1009 {
1010 if ( !feature.hasGeometry() )
1011 return true;
1012
1013 if ( !feature.geometry().isGeosValid() )
1014 {
1015 QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
1016 if ( mRequest.invalidGeometryCallback() )
1017 {
1018 mRequest.invalidGeometryCallback()( feature );
1019 }
1020 return false;
1021 }
1022 break;
1023 }
1024
1026 if ( !feature.hasGeometry() )
1027 return true;
1028
1029 if ( !feature.geometry().isGeosValid() )
1030 {
1031 QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
1032 close();
1033 if ( mRequest.invalidGeometryCallback() )
1034 {
1035 mRequest.invalidGeometryCallback()( feature );
1036 }
1037 return false;
1038 }
1039 break;
1040 }
1041
1042 return true;
1043}
1044
1046{
1047 switch ( mSource->mFields.fieldOrigin( fieldIdx ) )
1048 {
1050 prepareExpression( fieldIdx );
1051 break;
1052
1054 if ( mSource->mJoinBuffer->containsJoins() )
1055 {
1056 prepareJoin( fieldIdx );
1057 }
1058 break;
1059
1063 break;
1064 }
1065}
1066
1068{
1069 QList< FetchJoinInfo >::const_iterator joinIt = mOrderedJoinInfoList.constBegin();
1070 for ( ; joinIt != mOrderedJoinInfoList.constEnd(); ++joinIt )
1071 {
1072 const QVariant targetFieldValue = f.attribute( joinIt->targetField );
1073 if ( !targetFieldValue.isValid() )
1074 continue;
1075
1076 const QHash< QString, QgsAttributes> &memoryCache = joinIt->joinInfo->cachedAttributes;
1077 if ( memoryCache.isEmpty() )
1078 joinIt->addJoinedAttributesDirect( f, targetFieldValue );
1079 else
1080 joinIt->addJoinedAttributesCached( f, targetFieldValue );
1081 }
1082}
1083
1085{
1086 // make sure we have space for newly added attributes
1087 QgsAttributes attr = f.attributes();
1088 attr.resize( mSource->mFields.count() ); // Provider attrs count + joined attrs count + expression attrs count
1089 f.setAttributes( attr );
1090
1091 // possible TODO - handle combinations of expression -> join -> expression -> join?
1092 // but for now, write that off as too complex and an unlikely rare, unsupported use case
1093
1094 QList< int > fetchedVirtualAttributes;
1095 //first, check through joins for any virtual fields we need
1096 QMap<const QgsVectorLayerJoinInfo *, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
1097 for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
1098 {
1099 if ( mExpressionFieldInfo.contains( joinIt->targetField ) )
1100 {
1101 // have to calculate expression field before we can handle this join
1102 addExpressionAttribute( f, joinIt->targetField );
1103 fetchedVirtualAttributes << joinIt->targetField;
1104 }
1105 }
1106
1107 if ( !mFetchJoinInfo.isEmpty() )
1109
1110 // add remaining expression fields
1111 if ( !mExpressionFieldInfo.isEmpty() )
1112 {
1113 QMap<int, QgsExpression *>::ConstIterator it = mExpressionFieldInfo.constBegin();
1114 for ( ; it != mExpressionFieldInfo.constEnd(); ++it )
1115 {
1116 if ( fetchedVirtualAttributes.contains( it.key() ) )
1117 continue;
1118
1119 addExpressionAttribute( f, it.key() );
1120 }
1121 }
1122}
1123
1125{
1126 QgsExpression *exp = mExpressionFieldInfo.value( attrIndex );
1127 if ( exp )
1128 {
1129 if ( !mExpressionContext )
1130 createExpressionContext();
1131
1132 mExpressionContext->setFeature( f );
1133 QVariant val = exp->evaluate( mExpressionContext.get() );
1134 ( void )mSource->mFields.at( attrIndex ).convertCompatible( val );
1135 f.setAttribute( attrIndex, val );
1136 }
1137 else
1138 {
1139 f.setAttribute( attrIndex, QVariant() );
1140 }
1141}
1142
1144{
1145 Q_UNUSED( simplifyMethod )
1146 return false;
1147}
1148
1149bool QgsVectorLayerFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
1150{
1151 Q_UNUSED( methodType )
1152 return false;
1153}
1154
1155
1157{
1158 const QHash<QString, QgsAttributes> &memoryCache = joinInfo->cachedAttributes;
1159 const QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
1160 if ( it == memoryCache.constEnd() )
1161 return; // joined value not found -> leaving the attributes empty (null)
1162
1163 int index = indexOffset;
1164
1165 const QgsAttributes &featureAttributes = it.value();
1166 for ( int i = 0; i < featureAttributes.count(); ++i )
1167 {
1168 f.setAttribute( index++, featureAttributes.at( i ) );
1169 }
1170}
1171
1172
1173
1175{
1176#if 0 // this is not thread safe -- we cannot access the layer here as this will be called from non-main threads.
1177 // Shortcut
1178 if ( joinLayer && ! joinLayer->hasFeatures() )
1179 {
1180 return;
1181 }
1182#endif
1183
1184 // no memory cache, query the joined values by setting substring
1185 QString subsetString;
1186
1187 const QString joinFieldName = joinInfo->joinFieldName();
1188
1189 subsetString.append( u"\"%1\""_s.arg( joinFieldName ) );
1190
1191 if ( QgsVariantUtils::isNull( joinValue ) )
1192 {
1193 subsetString += " IS NULL"_L1;
1194 }
1195 else
1196 {
1197 QString v = joinValue.toString();
1198 switch ( joinValue.userType() )
1199 {
1200 case QMetaType::Type::Int:
1201 case QMetaType::Type::LongLong:
1202 case QMetaType::Type::Double:
1203 break;
1204
1205 default:
1206 case QMetaType::Type::QString:
1207 v.replace( '\'', "''"_L1 );
1208 v.prepend( '\'' ).append( '\'' );
1209 break;
1210 }
1211 subsetString += '=' + v;
1212 }
1213
1214 QList<int> joinedAttributeIndices;
1215
1216 // maybe user requested just a subset of layer's attributes
1217 // so we do not have to cache everything
1218 if ( joinInfo->hasSubset() )
1219 {
1220 const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinInfo, joinLayerFields );
1221 const QVector<int> subsetIndices = QgsVectorLayerJoinBuffer::joinSubsetIndices( joinLayerFields, subsetNames );
1222 joinedAttributeIndices = qgis::setToList( qgis::listToSet( attributes ).intersect( qgis::listToSet( subsetIndices.toList() ) ) );
1223 }
1224 else
1225 {
1226 joinedAttributeIndices = attributes;
1227 }
1228
1229 // we don't need the join field, it is already present in the other table
1230 joinedAttributeIndices.removeAll( joinField );
1231
1232 // select (no geometry)
1233 QgsFeatureRequest request;
1235 request.setSubsetOfAttributes( joinedAttributeIndices );
1236 request.setFilterExpression( subsetString );
1237 request.setLimit( 1 );
1238 request.setRequestMayBeNested( true );
1239 QgsFeatureIterator fi = joinSource->getFeatures( request );
1240
1241 // get first feature
1242 const QList<int> sourceAttrIndexes = attributesSourceToDestLayerMap.keys();
1243 QgsFeature fet;
1244 if ( fi.nextFeature( fet ) )
1245 {
1246 const QgsAttributes attr = fet.attributes();
1247
1248 for ( const int sourceAttrIndex : sourceAttrIndexes )
1249 {
1250 if ( sourceAttrIndex == joinField )
1251 continue;
1252
1253 const int destAttrIndex = attributesSourceToDestLayerMap.value( sourceAttrIndex );
1254
1255 f.setAttribute( destAttrIndex, attr.at( sourceAttrIndex ) );
1256 }
1257 }
1258 else
1259 {
1260 // no suitable join feature found, keeping empty (null) attributes
1261 }
1262}
1263
1264
1265
1266
1268{
1269 const QgsFeatureId featureId = mRequest.filterFid();
1270
1271 // deleted already?
1272 if ( mSource->mDeletedFeatureIds.contains( featureId ) )
1273 return false;
1274
1275 // has changed geometry?
1276 if ( !( mRequest.flags() & Qgis::FeatureRequestFlag::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) )
1277 {
1278 useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f );
1279 return true;
1280 }
1281
1282 // added features
1283 for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter )
1284 {
1285 if ( iter->id() == featureId )
1286 {
1287 useAddedFeature( *iter, f );
1288 return true;
1289 }
1290 }
1291
1292 // regular features
1293 QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
1294 if ( fi.nextFeature( f ) )
1295 {
1296 f.setFields( mSource->mFields );
1297
1298 if ( mSource->mHasEditBuffer )
1300
1303 else
1304 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
1305
1306 return true;
1307 }
1308
1309 return false;
1310}
1311
1313{
1314 QgsAttributes attrs = f.attributes();
1315
1316 // remove all attributes that will disappear - from higher indices to lower
1317 for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
1318 {
1319 attrs.remove( mSource->mDeletedAttributeIds[idx] );
1320 }
1321
1322 // adjust size to accommodate added attributes
1323 attrs.resize( attrs.count() + mSource->mAddedAttributes.count() );
1324
1325 // update changed attributes
1326 if ( mSource->mChangedAttributeValues.contains( f.id() ) )
1327 {
1328 const QgsAttributeMap &map = mSource->mChangedAttributeValues[f.id()];
1329 for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
1330 attrs[it.key()] = it.value();
1331 }
1332 f.setAttributes( attrs );
1333}
1334
1336{
1337 if ( mSource->mChangedGeometries.contains( f.id() ) )
1338 f.setGeometry( mSource->mChangedGeometries[f.id()] );
1339}
1340
1341void QgsVectorLayerFeatureIterator::createExpressionContext()
1342{
1343 mExpressionContext = std::make_unique< QgsExpressionContext >();
1344 mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
1345 mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) ); // skip-keyword-check
1346 mExpressionContext->appendScope( new QgsExpressionContextScope( mSource->mLayerScope ) );
1347 mExpressionContext->setFeedback( mRequest.feedback() );
1348}
1349
1350bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys )
1351{
1352 Q_UNUSED( orderBys )
1353 return mDelegatedOrderByToProvider;
1354}
1355
1356
1357//
1358// QgsVectorLayerSelectedFeatureSource
1359//
1360
1362 : mSource( layer )
1363 , mSelectedFeatureIds( layer->selectedFeatureIds() )
1364 , mWkbType( layer->wkbType() )
1365 , mName( layer->name() )
1366 , mLayer( layer )
1367{}
1368
1370{
1371 QgsFeatureRequest req( request );
1372
1373 // while QgsVectorLayerSelectedFeatureIterator will reject any features not in mSelectedFeatureIds,
1374 // we still tweak the feature request to only request selected feature ids wherever we can -- this
1375 // allows providers to optimise the request and avoid requesting features we don't need
1376 // note that we can't do this for some request types - e.g. expression based requests, so
1377 // in that case we just pass the request on to the provider and let QgsVectorLayerSelectedFeatureIterator
1378 // do ALL the filtering
1379 if ( req.filterFids().isEmpty() && req.filterType() == Qgis::FeatureRequestFilterType::NoFilter )
1380 {
1381 req.setFilterFids( mSelectedFeatureIds );
1382 }
1383 else if ( !req.filterFids().isEmpty() )
1384 {
1385 QgsFeatureIds reqIds = mSelectedFeatureIds;
1386 reqIds.intersect( req.filterFids() );
1387 req.setFilterFids( reqIds );
1388 }
1389
1390 return QgsFeatureIterator( new QgsVectorLayerSelectedFeatureIterator( mSelectedFeatureIds, req, mSource ) );
1391}
1392
1397
1399{
1400 return mSource.fields();
1401}
1402
1404{
1405 return mWkbType;
1406}
1407
1409{
1410 return mSelectedFeatureIds.count();
1411}
1412
1414{
1415 return mName;
1416}
1417
1419{
1420 if ( mLayer )
1421 return mLayer->createExpressionContextScope();
1422 else
1423 return nullptr;
1424}
1425
1427{
1428 if ( mLayer )
1429 return mLayer->hasSpatialIndex();
1430 else
1432}
1433
1434//
1435// QgsVectorLayerSelectedFeatureIterator
1436//
1437
1439QgsVectorLayerSelectedFeatureIterator::QgsVectorLayerSelectedFeatureIterator( const QgsFeatureIds &selectedFeatureIds, const QgsFeatureRequest &request, QgsVectorLayerFeatureSource &source )
1440 : QgsAbstractFeatureIterator( request )
1441 , mSelectedFeatureIds( selectedFeatureIds )
1442{
1443 QgsFeatureRequest sourceRequest = request;
1444 if ( sourceRequest.filterType() == Qgis::FeatureRequestFilterType::Expression && sourceRequest.limit() > 0 )
1445 {
1446 // we can't pass the request limit to the provider here - otherwise the provider will
1447 // limit the number of returned features and may only return a bunch of matching features
1448 // which AREN'T in the selected feature set
1449 sourceRequest.setLimit( -1 );
1450 }
1451 mIterator = source.getFeatures( sourceRequest );
1452}
1453
1454bool QgsVectorLayerSelectedFeatureIterator::rewind()
1455{
1456 return mIterator.rewind();
1457}
1458
1459bool QgsVectorLayerSelectedFeatureIterator::close()
1460{
1461 return mIterator.close();
1462}
1463
1464bool QgsVectorLayerSelectedFeatureIterator::fetchFeature( QgsFeature &f )
1465{
1466 while ( mIterator.nextFeature( f ) )
1467 {
1468 if ( mSelectedFeatureIds.contains( f.id() ) )
1469 return true;
1470 }
1471 return false;
1472}
1473
@ Fid
Filter using feature ID.
Definition qgis.h:2282
@ Expression
Filter using expression.
Definition qgis.h:2283
@ NoFilter
No filter is applied.
Definition qgis.h:2281
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:577
@ Unknown
Spatial index presence cannot be determined, index may or may not exist.
Definition qgis.h:578
@ SubsetOfAttributes
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag).
Definition qgis.h:2255
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2254
@ Critical
Critical/error message.
Definition qgis.h:162
@ NoFilter
No spatial filtering of features.
Definition qgis.h:2310
@ Provider
Field originates from the underlying data provider of the vector layer.
Definition qgis.h:1764
@ Edit
Field has been temporarily added in editing mode.
Definition qgis.h:1766
@ Unknown
The field origin has not been specified.
Definition qgis.h:1763
@ Expression
Field is calculated from an expression.
Definition qgis.h:1767
@ Join
Field originates from a joined layer.
Definition qgis.h:1765
@ NoCheck
No invalid geometry checking.
Definition qgis.h:2297
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Definition qgis.h:2299
@ SkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
Definition qgis.h:2298
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
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:69
QgsFeatureId id
Definition qgsfeature.h:68
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:71
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: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())
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.