QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgsvectorlayerfeatureiterator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectorlayerfeatureiterator.cpp
3 ---------------------
4 begin : Dezember 2012
5 copyright : (C) 2012 by Martin Dobias
6 email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15#include "qgsconfig.h"
17
18#include "qgsdistancearea.h"
19#include "qgsexception.h"
23#include "qgsgeometryengine.h"
25#include "qgsmessagelog.h"
26#include "qgsproject.h"
27#include "qgssimplifymethod.h"
29#include "qgsvectorlayer.h"
32
33#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
34#include <QThreadStorage>
35#endif
36
37#include <deque>
38#include <memory>
39
41{
42 const QMutexLocker locker( &layer->mFeatureSourceConstructorMutex );
43 const QgsVectorDataProvider *provider = layer->dataProvider();
44 if ( provider )
45 mProviderFeatureSource.reset( provider->featureSource() );
46 mFields = layer->fields();
47 mId = layer->id();
48
49 // update layer's join caches if necessary
50 if ( layer->mJoinBuffer->containsJoins() )
51 layer->mJoinBuffer->createJoinCaches();
52
53 mJoinBuffer.reset( layer->mJoinBuffer->clone() );
54 for ( const QgsVectorLayerJoinInfo &joinInfo : mJoinBuffer->vectorJoins() )
55 {
56 if ( QgsVectorLayer *joinLayer = joinInfo.joinLayer() )
57 {
58 JoinLayerSource source;
59 source.joinSource = std::make_shared< QgsVectorLayerFeatureSource >( joinLayer );
60 source.joinLayerFields = joinLayer->fields();
61 mJoinSources.insert( joinLayer->id(), source );
62 }
63 }
64
65 mExpressionFieldBuffer = std::make_unique<QgsExpressionFieldBuffer>( *layer->mExpressionFieldBuffer );
66 mCrs = layer->crs();
67
68 mHasEditBuffer = layer->editBuffer();
69 if ( mHasEditBuffer )
70 {
71#if 0
72 // TODO[MD]: after merge
73 if ( request.filterType() == QgsFeatureRequest::FilterFid )
74 {
75
76 // only copy relevant parts
77 if ( L->editBuffer()->addedFeatures().contains( request.filterFid() ) )
78 mAddedFeatures.insert( request.filterFid(), L->editBuffer()->addedFeatures()[ request.filterFid()] );
79
80 if ( L->editBuffer()->changedGeometries().contains( request.filterFid() ) )
81 mChangedGeometries.insert( request.filterFid(), L->editBuffer()->changedGeometries()[ request.filterFid()] );
82
83 if ( L->editBuffer()->deletedFeatureIds().contains( request.filterFid() ) )
84 mDeletedFeatureIds.insert( request.filterFid() );
85
86 if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
87 mChangedAttributeValues.insert( request.filterFid(), L->editBuffer()->changedAttributeValues()[ request.filterFid()] );
88
89 if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
90 mChangedFeaturesRequest.setFilterFids( QgsFeatureIds() << request.filterFid() );
91 }
92 else
93 {
94#endif
95 // If we are inside a transaction the iterator "sees" the current status
96 if ( provider && ! provider->transaction() )
97 {
102 mAddedAttributes = QList<QgsField>( layer->editBuffer()->addedAttributes() );
104 }
105#if 0
106 }
107#endif
108 }
109
110 const std::unique_ptr< QgsExpressionContextScope > layerScope( QgsExpressionContextUtils::layerScope( layer ) );
111 mLayerScope = *layerScope;
112}
113
115
117{
118 // return feature iterator that does not own this source
119 return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, false, request ) );
120}
121
126
131
133{
134 return mId;
135}
136
137
140 , mFetchedFid( false )
141{
142 mTransform = mRequest.calculateTransform( mSource->mCrs );
143 mHasValidTransform = mTransform.isValid();
144
145 // prepare spatial filter geometries for optimal speed
146 // since the mDistanceWithin* constraint member variables are all in the DESTINATION CRS,
147 // we set all these upfront before any transformation to the source CRS is done.
148
149 switch ( mRequest.spatialFilterType() )
150 {
151 case Qgis::SpatialFilterType::NoFilter:
152 case Qgis::SpatialFilterType::BoundingBox:
153 break;
154
155 case Qgis::SpatialFilterType::DistanceWithin:
156 if ( !mRequest.referenceGeometry().isEmpty() )
157 {
158 // Note that regardless of whether or not we'll ultimately be able to handoff this check to the underlying provider,
159 // we still need these reference geometry constraints in the vector layer iterator as we need them to check against
160 // the features from the vector layer's edit buffer! (In other words, we cannot completely hand off responsibility for
161 // these checks to the provider and ignore them locally)
162 mDistanceWithinGeom = mRequest.referenceGeometry();
163 mDistanceWithinEngine = mRequest.referenceGeometryEngine();
164 mDistanceWithinEngine->prepareGeometry();
165 mDistanceWithin = mRequest.distanceWithin();
166 }
167 break;
168 }
169
170 bool canDelegateLimitToProvider = true;
171 try
172 {
173 switch ( updateRequestToSourceCrs( mRequest, mTransform ) )
174 {
176 break;
177
179 // we have to disable any limit on the provider's request -- since that request may be returning features which are outside the
180 // distance tolerance, we'll have to fetch them all and then handle the limit check manually only after testing for the distance within constraint
181 canDelegateLimitToProvider = false;
182 break;
183 }
184
185 // mFilterRect is in the source CRS, so we set that now (after request transformation has been done)
186 mFilterRect = mRequest.filterRect();
187 }
188 catch ( QgsCsException & )
189 {
190 // can't reproject request filters
191 close();
192 return;
193 }
194
195 // check whether the order by clause(s) can be delegated to the provider
196 mDelegatedOrderByToProvider = !mSource->mHasEditBuffer;
197 if ( !mRequest.orderBy().isEmpty() )
198 {
199 QSet<int> attributeIndexes;
200 const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields );
201 for ( const int attrIndex : usedAttributeIndices )
202 {
203 if ( mSource->mFields.fieldOrigin( attrIndex ) != Qgis::FieldOrigin::Provider )
204 mDelegatedOrderByToProvider = false;
205
206 attributeIndexes << attrIndex;
207 }
208
209 if ( ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes ) && !mDelegatedOrderByToProvider )
210 {
211 attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
212 mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
213 }
214 }
215
216 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
217 {
218 mRequest.expressionContext()->setFields( mSource->mFields );
219 mRequest.filterExpression()->prepare( mRequest.expressionContext() );
220
221 if ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes )
222 {
223 // ensure that all fields required for filter expressions are prepared
224 QSet<int> attributeIndexes = mRequest.filterExpression()->referencedAttributeIndexes( mSource->mFields );
225 attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
226 mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
227 }
228 }
229
230 prepareFields();
231
232 mHasVirtualAttributes = !mFetchJoinInfo.isEmpty() || !mExpressionFieldInfo.isEmpty();
233
234 // by default provider's request is the same
235 mProviderRequest = mRequest;
236 // but we remove any destination CRS parameter - that is handled in QgsVectorLayerFeatureIterator,
237 // not at the provider level. Otherwise virtual fields depending on geometry would have incorrect
238 // values
239 if ( mRequest.coordinateTransform().isValid() || mRequest.destinationCrs().isValid() )
240 {
241 mProviderRequest.setCoordinateTransform( QgsCoordinateTransform() );
242 mProviderRequest.setDestinationCrs( QgsCoordinateReferenceSystem(), mRequest.transformContext() );
243 }
244
245 if ( !mDelegatedOrderByToProvider )
246 {
247 mProviderRequest.setOrderBy( QgsFeatureRequest::OrderBy() );
248 }
249
250 if ( !canDelegateLimitToProvider )
251 {
252 mProviderRequest.setLimit( -1 );
253 }
254
255 if ( mProviderRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes )
256 {
257 // prepare list of attributes to match provider fields
258 QSet<int> providerSubset;
259 const QgsAttributeList subset = mProviderRequest.subsetOfAttributes();
260 const int nPendingFields = mSource->mFields.count();
261 for ( const int attrIndex : subset )
262 {
263 if ( attrIndex < 0 || attrIndex >= nPendingFields )
264 continue;
265 if ( mSource->mFields.fieldOrigin( attrIndex ) == Qgis::FieldOrigin::Provider )
266 providerSubset << mSource->mFields.fieldOriginIndex( attrIndex );
267 }
268
269 // This is done in order to be prepared to do fallback order bys
270 // and be sure we have the required columns.
271 // TODO:
272 // It would be nicer to first check if we can compile the order by
273 // and only modify the subset if we cannot.
274 if ( !mProviderRequest.orderBy().isEmpty() )
275 {
276 const auto usedAttributeIndices = mProviderRequest.orderBy().usedAttributeIndices( mSource->mFields );
277 for ( const int attrIndex : usedAttributeIndices )
278 {
279 providerSubset << attrIndex;
280 }
281 }
282
283 mProviderRequest.setSubsetOfAttributes( qgis::setToList( providerSubset ) );
284 }
285
286 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
287 {
288 const bool needsGeom = mProviderRequest.filterExpression()->needsGeometry();
289 const auto constReferencedColumns = mProviderRequest.filterExpression()->referencedColumns();
290 for ( const QString &field : constReferencedColumns )
291 {
292 const int idx = source->mFields.lookupField( field );
293
294 // If there are fields in the expression which are not of origin provider, the provider will not be able to filter based on them.
295 // In this case we disable the expression filter.
296 if ( source->mFields.fieldOrigin( idx ) != Qgis::FieldOrigin::Provider )
297 {
298 mProviderRequest.disableFilter();
299 // can't limit at provider side
300 mProviderRequest.setLimit( -1 );
301 if ( needsGeom )
302 {
303 // have to get geometry from provider in order to evaluate expression on client
304 mProviderRequest.setFlags( mProviderRequest.flags() & ~( static_cast<int>( Qgis::FeatureRequestFlag::NoGeometry ) ) );
305 }
306 break;
307 }
308 }
309 }
310
311 if ( mSource->mHasEditBuffer )
312 {
313 mChangedFeaturesRequest = mProviderRequest;
314 QgsFeatureIds changedIds;
315 QgsChangedAttributesMap::const_iterator attIt = mSource->mChangedAttributeValues.constBegin();
316 for ( ; attIt != mSource->mChangedAttributeValues.constEnd(); ++attIt )
317 {
318 changedIds << attIt.key();
319 }
320 mChangedFeaturesRequest.setFilterFids( changedIds );
321
322 if ( mChangedFeaturesRequest.limit() > 0 )
323 {
324 int providerLimit = mProviderRequest.limit();
325
326 // features may be deleted in buffer, so increase limit sent to provider
327 providerLimit += mSource->mDeletedFeatureIds.size();
328
329 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
330 {
331 // attribute changes may mean some features no longer match expression, so increase limit sent to provider
332 providerLimit += mSource->mChangedAttributeValues.size();
333 }
334
335 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression || mProviderRequest.spatialFilterType() != Qgis::SpatialFilterType::NoFilter )
336 {
337 // geometry changes may mean some features no longer match expression or rect, so increase limit sent to provider
338 providerLimit += mSource->mChangedGeometries.size();
339 }
340
341 mProviderRequest.setLimit( providerLimit );
342 mChangedFeaturesRequest.setLimit( providerLimit );
343 }
344 }
345
346 if ( request.filterType() == Qgis::FeatureRequestFilterType::Fid )
347 {
348 mFetchedFid = false;
349 }
350 else // no filter or filter by rect
351 {
352 if ( mSource->mProviderFeatureSource )
353 {
354 if ( mSource->mHasEditBuffer )
355 {
356 mChangedFeaturesIterator = mSource->mProviderFeatureSource->getFeatures( mChangedFeaturesRequest );
357 }
358 else
359 {
360 mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
361 }
362 }
363
364 rewindEditBuffer();
365 }
366}
367
368
375
377
383class QgsThreadStackOverflowGuard
384{
385 public:
386
387#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
388 QgsThreadStackOverflowGuard( QThreadStorage<std::deque<QString>> &storage, const QString &stackFrameInformation, int maxDepth )
389#else
390 QgsThreadStackOverflowGuard( std::deque<QString> &storage, const QString &stackFrameInformation, int maxDepth )
391#endif
392 : mStorage( storage )
393 , mMaxDepth( maxDepth )
394 {
395#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
396 if ( !storage.hasLocalData() )
397 {
398 storage.setLocalData( std::deque<QString>() );
399 }
400
401 storage.localData().emplace_back( stackFrameInformation );
402#else
403 storage.emplace_back( stackFrameInformation );
404#endif
405 }
406
407 ~QgsThreadStackOverflowGuard()
408 {
409#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
410 mStorage.localData().pop_back();
411#else
412 mStorage.pop_back();
413#endif
414 }
415
416 bool hasStackOverflow() const
417 {
418#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
419 if ( mStorage.localData().size() > mMaxDepth )
420#else
421 if ( mStorage.size() > mMaxDepth )
422#endif
423 return true;
424 else
425 return false;
426 }
427
428 QString topFrames() const
429 {
430 QStringList dumpStack;
431#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
432 const std::deque<QString> &stack = mStorage.localData();
433#else
434 const std::deque<QString> &stack = mStorage;
435#endif
436
437 const int dumpSize = std::min( static_cast<int>( stack.size() ), 10 );
438 auto stackIt = stack.begin();
439 for ( int i = 0; i < dumpSize; ++i, stackIt++ )
440 {
441 dumpStack += *stackIt;
442 }
443
444 return dumpStack.join( '\n' );
445 }
446
447 std::size_t depth() const
448 {
449#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
450 return mStorage.localData().size();
451#else
452 return mStorage.size();
453#endif
454 }
455
456 private:
457#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
458 QThreadStorage<std::deque<QString>> &mStorage;
459#else
460 std::deque<QString> &mStorage;
461#endif
462 std::size_t mMaxDepth;
463};
464
466
468{
469 f.setValid( false );
470
471 if ( mClosed )
472 return false;
473
474#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
475 static QThreadStorage<std::deque<QString>> sStack;
476#else
477 static thread_local std::deque<QString> sStack;
478#endif
479
480 const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
481
482 if ( guard.hasStackOverflow() )
483 {
484 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 );
485 return false;
486 }
487
488 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
489 {
490 if ( mFetchedFid )
491 return false;
492 const bool res = nextFeatureFid( f );
493 if ( res && postProcessFeature( f ) )
494 {
495 mFetchedFid = true;
496 return res;
497 }
498 else
499 {
500 return false;
501 }
502 }
503
504 if ( !mFilterRect.isNull() )
505 {
507 return true;
508
509 // no more changed geometries
510 }
511
513 {
515 return true;
516
518 return true;
519
520 // no more changed features
521 }
522
523 while ( fetchNextAddedFeature( f ) )
524 {
525 return true;
526 }
527 // no more added features
528
529 if ( mProviderIterator.isClosed() )
530 {
532 if ( mSource->mProviderFeatureSource )
533 {
534 mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
535 mProviderIterator.setInterruptionChecker( mInterruptionChecker );
536 }
537 }
538
539 while ( mProviderIterator.nextFeature( f ) )
540 {
541 if ( mFetchConsidered.contains( f.id() ) )
542 continue;
543
544 // TODO[MD]: just one resize of attributes
545 f.setFields( mSource->mFields );
546
547 // update attributes
548 if ( mSource->mHasEditBuffer )
550
553 else
554 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
555
557 {
558 //filtering by expression, and couldn't do it on the provider side
559 mRequest.expressionContext()->setFeature( f );
560 if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
561 {
562 //feature did not match filter
563 continue;
564 }
565 }
566
567 // update geometry
568 // TODO[MK]: FilterRect check after updating the geometry
571
572 if ( !postProcessFeature( f ) )
573 continue;
574
575 return true;
576 }
577 // no more provider features
578
579 close();
580 return false;
581}
582
583
584
586{
587 if ( mClosed )
588 return false;
589
590 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
591 {
592 mFetchedFid = false;
593 }
594 else
595 {
596 mProviderIterator.rewind();
598 }
599
600 return true;
601}
602
604{
605 if ( mClosed )
606 return false;
607
608 mProviderIterator.close();
609
611
612 mClosed = true;
613 return true;
614}
615
617{
618 mProviderIterator.setInterruptionChecker( interruptionChecker );
619 mInterruptionChecker = interruptionChecker;
620}
621
623{
624 return mChangedFeaturesIterator.isValid() || mProviderIterator.isValid();
625}
626
628{
629 while ( mFetchAddedFeaturesIt != mSource->mAddedFeatures.constBegin() )
630 {
632 const QgsFeatureId fid = mFetchAddedFeaturesIt->id();
633
634 if ( mFetchConsidered.contains( fid ) )
635 // must have changed geometry outside rectangle
636 continue;
637
639
640 // can't test for feature acceptance until after calling useAddedFeature
641 // since acceptFeature may rely on virtual fields
642 if ( !mRequest.acceptFeature( f ) )
643 // skip features which are not accepted by the filter
644 continue;
645
646 if ( !postProcessFeature( f ) )
647 continue;
648
649 return true;
650 }
651
652 mFetchAddedFeaturesIt = mSource->mAddedFeatures.constBegin();
653 return false; // no more added features
654}
655
656
658{
659 // since QgsFeature is implicitly shared, it's more efficient to just copy the
660 // whole feature, even if flags like NoGeometry or a subset of attributes is set at the request.
661 // This helps potentially avoid an unnecessary detach of the feature
662 f = src;
663 f.setValid( true );
664 f.setFields( mSource->mFields );
665
668 else
669 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
670}
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
756 ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Expression && mRequest.filterExpression()->needsGeometry() ) )
757 {
758 f.setGeometry( geom );
759 }
760
761 const bool subsetAttrs = ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes );
762 if ( !subsetAttrs || !mRequest.subsetOfAttributes().isEmpty() )
763 {
764 // retrieve attributes from provider
765 QgsFeature tmp;
766 //mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes );
767 QgsFeatureRequest request;
769 if ( subsetAttrs )
770 {
771 request.setSubsetOfAttributes( mProviderRequest.subsetOfAttributes() );
772 }
773 QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( request );
774 if ( fi.nextFeature( tmp ) )
775 {
776 if ( mHasVirtualAttributes || mSource->mHasEditBuffer )
778 f.setAttributes( tmp.attributes() );
779 }
780 }
781
783}
784
785
786
788{
789 mFetchConsidered = mSource->mDeletedFeatureIds;
790
791 mFetchAddedFeaturesIt = mSource->mAddedFeatures.constEnd();
792 mFetchChangedGeomIt = mSource->mChangedGeometries.constBegin();
793}
794
796{
797 if ( !mSource->mFields.exists( fieldIdx ) )
798 return;
799
800 if ( mSource->mFields.fieldOrigin( fieldIdx ) != Qgis::FieldOrigin::Join )
801 return;
802
803 int sourceLayerIndex;
804 const QgsVectorLayerJoinInfo *joinInfo = mSource->mJoinBuffer->joinForFieldIndex( fieldIdx, mSource->mFields, sourceLayerIndex );
805 Q_ASSERT( joinInfo );
806
807 auto joinSourceIt = mSource->mJoinSources.constFind( joinInfo->joinLayerId() );
808 if ( joinSourceIt == mSource->mJoinSources.constEnd() )
809 return; // invalid join (unresolved reference to layer)
810
811 if ( !mFetchJoinInfo.contains( joinInfo ) )
812 {
813 FetchJoinInfo info;
814 info.joinInfo = joinInfo;
815 info.joinSource = joinSourceIt->joinSource;
816 info.indexOffset = mSource->mJoinBuffer->joinedFieldsOffset( joinInfo, mSource->mFields );
817 info.targetField = mSource->mFields.indexFromName( joinInfo->targetFieldName() );
818 info.joinField = joinSourceIt->joinLayerFields.indexFromName( joinInfo->joinFieldName() );
819 info.joinLayerFields = joinSourceIt->joinLayerFields;
820
821 // for joined fields, we always need to request the targetField from the provider too
822 if ( !mPreparedFields.contains( info.targetField ) && !mFieldsToPrepare.contains( info.targetField ) )
823 mFieldsToPrepare << info.targetField;
824
825 if ( ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes ) && !mRequest.subsetOfAttributes().contains( info.targetField ) )
826 mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() << info.targetField );
827
828 mFetchJoinInfo.insert( joinInfo, info );
829 }
830
831 // store field source index - we'll need it when fetching from provider
832 mFetchJoinInfo[ joinInfo ].attributes.push_back( sourceLayerIndex );
833 mFetchJoinInfo[ joinInfo ].attributesSourceToDestLayerMap[sourceLayerIndex] = fieldIdx;
834}
835
836
838{
839#if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
840 static QThreadStorage<std::deque<QString>> sStack;
841#else
842 static thread_local std::deque<QString> sStack;
843#endif
844
845 const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
846
847 if ( guard.hasStackOverflow() )
848 {
849 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 );
850 return;
851 }
852
853 const QList<QgsExpressionFieldBuffer::ExpressionField> &exps = mSource->mExpressionFieldBuffer->expressions();
854
855 const int oi = mSource->mFields.fieldOriginIndex( fieldIdx );
856 auto exp = std::make_unique<QgsExpression>( exps[oi].cachedExpression );
857
859 da.setSourceCrs( mSource->mCrs, QgsProject::instance()->transformContext() ); // skip-keyword-check
860 da.setEllipsoid( QgsProject::instance()->ellipsoid() ); // skip-keyword-check
861 exp->setGeomCalculator( &da );
862 exp->setDistanceUnits( QgsProject::instance()->distanceUnits() ); // skip-keyword-check
863 exp->setAreaUnits( QgsProject::instance()->areaUnits() ); // skip-keyword-check
864
865 if ( !mExpressionContext )
866 createExpressionContext();
867 exp->prepare( mExpressionContext.get() );
868 const QSet<int> referencedColumns = exp->referencedAttributeIndexes( mSource->fields() );
869
870 QSet<int> requestedAttributes = qgis::listToSet( mRequest.subsetOfAttributes() );
871
872 for ( const int dependentFieldIdx : referencedColumns )
873 {
875 {
876 requestedAttributes += dependentFieldIdx;
877 }
878 // also need to fetch this dependent field
879 if ( !mPreparedFields.contains( dependentFieldIdx ) && !mFieldsToPrepare.contains( dependentFieldIdx ) )
880 mFieldsToPrepare << dependentFieldIdx;
881 }
882
884 {
885 mRequest.setSubsetOfAttributes( qgis::setToList( requestedAttributes ) );
886 }
887
888 if ( exp->needsGeometry() )
889 {
890 mRequest.setFlags( mRequest.flags() & ~( static_cast<int>( Qgis::FeatureRequestFlag::NoGeometry ) ) );
891 }
892
893 mExpressionFieldInfo.insert( fieldIdx, exp.release() );
894}
895
897{
898 mPreparedFields.clear();
899 mFieldsToPrepare.clear();
900 mFetchJoinInfo.clear();
901 mOrderedJoinInfoList.clear();
902
903 mExpressionContext.reset();
904
905 mFieldsToPrepare = ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes )
906 ? mRequest.subsetOfAttributes()
907 : mSource->mFields.allAttributesList();
908
909 while ( !mFieldsToPrepare.isEmpty() )
910 {
911 const int fieldIdx = mFieldsToPrepare.takeFirst();
912 if ( mPreparedFields.contains( fieldIdx ) )
913 continue;
914
915 mPreparedFields << fieldIdx;
916 prepareField( fieldIdx );
917 }
918
919 //sort joins by dependency
920 if ( !mFetchJoinInfo.empty() )
921 {
922 createOrderedJoinList();
923 }
924
925}
926
927void QgsVectorLayerFeatureIterator::createOrderedJoinList()
928{
929 mOrderedJoinInfoList = mFetchJoinInfo.values();
930 if ( mOrderedJoinInfoList.size() < 2 )
931 {
932 return;
933 }
934
935 QSet<int> resolvedFields; //todo: get provider / virtual fields without joins
936
937 //add all provider fields without joins as resolved fields
938 QList< int >::const_iterator prepFieldIt = mPreparedFields.constBegin();
939 for ( ; prepFieldIt != mPreparedFields.constEnd(); ++prepFieldIt )
940 {
941 if ( mSource->mFields.fieldOrigin( *prepFieldIt ) != Qgis::FieldOrigin::Join )
942 {
943 resolvedFields.insert( *prepFieldIt );
944 }
945 }
946
947 //iterate through the joins. If target field is not yet covered, move the entry to the end of the list
948
949 //some join combinations might not have a resolution at all
950 const int maxIterations = ( mOrderedJoinInfoList.size() + 1 ) * mOrderedJoinInfoList.size() / 2.0;
951 int currentIteration = 0;
952
953 for ( int i = 0; i < mOrderedJoinInfoList.size() - 1; ++i )
954 {
955 if ( !resolvedFields.contains( mOrderedJoinInfoList.at( i ).targetField ) )
956 {
957 mOrderedJoinInfoList.append( mOrderedJoinInfoList.at( i ) );
958 mOrderedJoinInfoList.removeAt( i );
959 --i;
960 }
961 else
962 {
963 const int offset = mOrderedJoinInfoList.at( i ).indexOffset;
964 const int joinField = mOrderedJoinInfoList.at( i ).joinField;
965
966 const QgsAttributeList attributes = mOrderedJoinInfoList.at( i ).attributes;
967 for ( int n = 0; n < attributes.size(); n++ )
968 {
969 if ( n != joinField )
970 {
971 resolvedFields.insert( joinField < n ? n + offset - 1 : n + offset );
972 }
973 }
974 }
975
976 ++currentIteration;
977 if ( currentIteration >= maxIterations )
978 {
979 break;
980 }
981 }
982}
983
984bool QgsVectorLayerFeatureIterator::postProcessFeature( QgsFeature &feature )
985{
986 bool result = checkGeometryValidity( feature );
987 if ( result && mHasValidTransform )
989
990 if ( result && mDistanceWithinEngine && feature.hasGeometry() )
991 {
992 result = mDistanceWithinEngine->distanceWithin( feature.geometry().constGet(), mDistanceWithin );
993 }
994
995 return result;
996}
997
998bool QgsVectorLayerFeatureIterator::checkGeometryValidity( const QgsFeature &feature )
999{
1000 switch ( mRequest.invalidGeometryCheck() )
1001 {
1003 return true;
1004
1006 {
1007 if ( !feature.hasGeometry() )
1008 return true;
1009
1010 if ( !feature.geometry().isGeosValid() )
1011 {
1012 QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
1013 if ( mRequest.invalidGeometryCallback() )
1014 {
1015 mRequest.invalidGeometryCallback()( feature );
1016 }
1017 return false;
1018 }
1019 break;
1020 }
1021
1023 if ( !feature.hasGeometry() )
1024 return true;
1025
1026 if ( !feature.geometry().isGeosValid() )
1027 {
1028 QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
1029 close();
1030 if ( mRequest.invalidGeometryCallback() )
1031 {
1032 mRequest.invalidGeometryCallback()( feature );
1033 }
1034 return false;
1035 }
1036 break;
1037 }
1038
1039 return true;
1040}
1041
1043{
1044 switch ( mSource->mFields.fieldOrigin( fieldIdx ) )
1045 {
1047 prepareExpression( fieldIdx );
1048 break;
1049
1051 if ( mSource->mJoinBuffer->containsJoins() )
1052 {
1053 prepareJoin( fieldIdx );
1054 }
1055 break;
1056
1060 break;
1061 }
1062}
1063
1065{
1066 QList< FetchJoinInfo >::const_iterator joinIt = mOrderedJoinInfoList.constBegin();
1067 for ( ; joinIt != mOrderedJoinInfoList.constEnd(); ++joinIt )
1068 {
1069 const QVariant targetFieldValue = f.attribute( joinIt->targetField );
1070 if ( !targetFieldValue.isValid() )
1071 continue;
1072
1073 const QHash< QString, QgsAttributes> &memoryCache = joinIt->joinInfo->cachedAttributes;
1074 if ( memoryCache.isEmpty() )
1075 joinIt->addJoinedAttributesDirect( f, targetFieldValue );
1076 else
1077 joinIt->addJoinedAttributesCached( f, targetFieldValue );
1078 }
1079}
1080
1082{
1083 // make sure we have space for newly added attributes
1084 QgsAttributes attr = f.attributes();
1085 attr.resize( mSource->mFields.count() ); // Provider attrs count + joined attrs count + expression attrs count
1086 f.setAttributes( attr );
1087
1088 // possible TODO - handle combinations of expression -> join -> expression -> join?
1089 // but for now, write that off as too complex and an unlikely rare, unsupported use case
1090
1091 QList< int > fetchedVirtualAttributes;
1092 //first, check through joins for any virtual fields we need
1093 QMap<const QgsVectorLayerJoinInfo *, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
1094 for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
1095 {
1096 if ( mExpressionFieldInfo.contains( joinIt->targetField ) )
1097 {
1098 // have to calculate expression field before we can handle this join
1099 addExpressionAttribute( f, joinIt->targetField );
1100 fetchedVirtualAttributes << joinIt->targetField;
1101 }
1102 }
1103
1104 if ( !mFetchJoinInfo.isEmpty() )
1106
1107 // add remaining expression fields
1108 if ( !mExpressionFieldInfo.isEmpty() )
1109 {
1110 QMap<int, QgsExpression *>::ConstIterator it = mExpressionFieldInfo.constBegin();
1111 for ( ; it != mExpressionFieldInfo.constEnd(); ++it )
1112 {
1113 if ( fetchedVirtualAttributes.contains( it.key() ) )
1114 continue;
1115
1116 addExpressionAttribute( f, it.key() );
1117 }
1118 }
1119}
1120
1122{
1123 QgsExpression *exp = mExpressionFieldInfo.value( attrIndex );
1124 if ( exp )
1125 {
1126 if ( !mExpressionContext )
1127 createExpressionContext();
1128
1129 mExpressionContext->setFeature( f );
1130 QVariant val = exp->evaluate( mExpressionContext.get() );
1131 ( void )mSource->mFields.at( attrIndex ).convertCompatible( val );
1132 f.setAttribute( attrIndex, val );
1133 }
1134 else
1135 {
1136 f.setAttribute( attrIndex, QVariant() );
1137 }
1138}
1139
1141{
1142 Q_UNUSED( simplifyMethod )
1143 return false;
1144}
1145
1146bool QgsVectorLayerFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
1147{
1148 Q_UNUSED( methodType )
1149 return false;
1150}
1151
1152
1154{
1155 const QHash<QString, QgsAttributes> &memoryCache = joinInfo->cachedAttributes;
1156 const QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
1157 if ( it == memoryCache.constEnd() )
1158 return; // joined value not found -> leaving the attributes empty (null)
1159
1160 int index = indexOffset;
1161
1162 const QgsAttributes &featureAttributes = it.value();
1163 for ( int i = 0; i < featureAttributes.count(); ++i )
1164 {
1165 f.setAttribute( index++, featureAttributes.at( i ) );
1166 }
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( QStringLiteral( "\"%1\"" ).arg( joinFieldName ) );
1187
1188 if ( QgsVariantUtils::isNull( joinValue ) )
1189 {
1190 subsetString += QLatin1String( " IS NULL" );
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( '\'', QLatin1String( "''" ) );
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
1262
1263
1265{
1266 const QgsFeatureId featureId = mRequest.filterFid();
1267
1268 // deleted already?
1269 if ( mSource->mDeletedFeatureIds.contains( featureId ) )
1270 return false;
1271
1272 // has changed geometry?
1273 if ( !( mRequest.flags() & Qgis::FeatureRequestFlag::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) )
1274 {
1275 useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f );
1276 return true;
1277 }
1278
1279 // added features
1280 for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter )
1281 {
1282 if ( iter->id() == featureId )
1283 {
1284 useAddedFeature( *iter, f );
1285 return true;
1286 }
1287 }
1288
1289 // regular features
1290 QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
1291 if ( fi.nextFeature( f ) )
1292 {
1293 f.setFields( mSource->mFields );
1294
1295 if ( mSource->mHasEditBuffer )
1297
1300 else
1301 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
1302
1303 return true;
1304 }
1305
1306 return false;
1307}
1308
1310{
1311 QgsAttributes attrs = f.attributes();
1312
1313 // remove all attributes that will disappear - from higher indices to lower
1314 for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
1315 {
1316 attrs.remove( mSource->mDeletedAttributeIds[idx] );
1317 }
1318
1319 // adjust size to accommodate added attributes
1320 attrs.resize( attrs.count() + mSource->mAddedAttributes.count() );
1321
1322 // update changed attributes
1323 if ( mSource->mChangedAttributeValues.contains( f.id() ) )
1324 {
1325 const QgsAttributeMap &map = mSource->mChangedAttributeValues[f.id()];
1326 for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
1327 attrs[it.key()] = it.value();
1328 }
1329 f.setAttributes( attrs );
1330}
1331
1333{
1334 if ( mSource->mChangedGeometries.contains( f.id() ) )
1335 f.setGeometry( mSource->mChangedGeometries[f.id()] );
1336}
1337
1338void QgsVectorLayerFeatureIterator::createExpressionContext()
1339{
1340 mExpressionContext = std::make_unique< QgsExpressionContext >();
1341 mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
1342 mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) ); // skip-keyword-check
1343 mExpressionContext->appendScope( new QgsExpressionContextScope( mSource->mLayerScope ) );
1344 mExpressionContext->setFeedback( mRequest.feedback() );
1345}
1346
1347bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys )
1348{
1349 Q_UNUSED( orderBys )
1350 return mDelegatedOrderByToProvider;
1351}
1352
1353
1354//
1355// QgsVectorLayerSelectedFeatureSource
1356//
1357
1359 : mSource( layer )
1360 , mSelectedFeatureIds( layer->selectedFeatureIds() )
1361 , mWkbType( layer->wkbType() )
1362 , mName( layer->name() )
1363 , mLayer( layer )
1364{}
1365
1367{
1368 QgsFeatureRequest req( request );
1369
1370 // while QgsVectorLayerSelectedFeatureIterator will reject any features not in mSelectedFeatureIds,
1371 // we still tweak the feature request to only request selected feature ids wherever we can -- this
1372 // allows providers to optimise the request and avoid requesting features we don't need
1373 // note that we can't do this for some request types - e.g. expression based requests, so
1374 // in that case we just pass the request on to the provider and let QgsVectorLayerSelectedFeatureIterator
1375 // do ALL the filtering
1376 if ( req.filterFids().isEmpty() && req.filterType() == Qgis::FeatureRequestFilterType::NoFilter )
1377 {
1378 req.setFilterFids( mSelectedFeatureIds );
1379 }
1380 else if ( !req.filterFids().isEmpty() )
1381 {
1382 QgsFeatureIds reqIds = mSelectedFeatureIds;
1383 reqIds.intersect( req.filterFids() );
1384 req.setFilterFids( reqIds );
1385 }
1386
1387 return QgsFeatureIterator( new QgsVectorLayerSelectedFeatureIterator( mSelectedFeatureIds, req, mSource ) );
1388}
1389
1394
1396{
1397 return mSource.fields();
1398}
1399
1401{
1402 return mWkbType;
1403}
1404
1406{
1407 return mSelectedFeatureIds.count();
1408}
1409
1411{
1412 return mName;
1413}
1414
1416{
1417 if ( mLayer )
1418 return mLayer->createExpressionContextScope();
1419 else
1420 return nullptr;
1421}
1422
1424{
1425 if ( mLayer )
1426 return mLayer->hasSpatialIndex();
1427 else
1429}
1430
1431//
1432// QgsVectorLayerSelectedFeatureIterator
1433//
1434
1436QgsVectorLayerSelectedFeatureIterator::QgsVectorLayerSelectedFeatureIterator( const QgsFeatureIds &selectedFeatureIds, const QgsFeatureRequest &request, QgsVectorLayerFeatureSource &source )
1437 : QgsAbstractFeatureIterator( request )
1438 , mSelectedFeatureIds( selectedFeatureIds )
1439{
1440 QgsFeatureRequest sourceRequest = request;
1441 if ( sourceRequest.filterType() == Qgis::FeatureRequestFilterType::Expression && sourceRequest.limit() > 0 )
1442 {
1443 // we can't pass the request limit to the provider here - otherwise the provider will
1444 // limit the number of returned features and may only return a bunch of matching features
1445 // which AREN'T in the selected feature set
1446 sourceRequest.setLimit( -1 );
1447 }
1448 mIterator = source.getFeatures( sourceRequest );
1449}
1450
1451bool QgsVectorLayerSelectedFeatureIterator::rewind()
1452{
1453 return mIterator.rewind();
1454}
1455
1456bool QgsVectorLayerSelectedFeatureIterator::close()
1457{
1458 return mIterator.close();
1459}
1460
1461bool QgsVectorLayerSelectedFeatureIterator::fetchFeature( QgsFeature &f )
1462{
1463 while ( mIterator.nextFeature( f ) )
1464 {
1465 if ( mSelectedFeatureIds.contains( f.id() ) )
1466 return true;
1467 }
1468 return false;
1469}
1470
@ Fid
Filter using feature ID.
Definition qgis.h:2224
@ Expression
Filter using expression.
Definition qgis.h:2225
@ NoFilter
No filter is applied.
Definition qgis.h:2223
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:558
@ Unknown
Spatial index presence cannot be determined, index may or may not exist.
Definition qgis.h:559
@ SubsetOfAttributes
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag).
Definition qgis.h:2197
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2196
@ Critical
Critical/error message.
Definition qgis.h:159
@ NoFilter
No spatial filtering of features.
Definition qgis.h:2252
@ Provider
Field originates from the underlying data provider of the vector layer.
Definition qgis.h:1706
@ Edit
Field has been temporarily added in editing mode.
Definition qgis.h:1708
@ Unknown
The field origin has not been specified.
Definition qgis.h:1705
@ Expression
Field is calculated from an expression.
Definition qgis.h:1709
@ Join
Field originates from a joined layer.
Definition qgis.h:1707
@ NoCheck
No invalid geometry checking.
Definition qgis.h:2239
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Definition qgis.h:2241
@ SkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
Definition qgis.h:2240
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:277
QgsAbstractFeatureIteratorFromSource(QgsVectorLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request)
Internal feature iterator to be implemented within data providers.
@ Success
Request was successfully updated to the source CRS, or no changes were required.
@ DistanceWithinMustBeCheckedManually
The distance within request cannot be losslessly updated to the source CRS, and callers will need to ...
void geometryToDestinationCrs(QgsFeature &feature, const QgsCoordinateTransform &transform) const
Transforms feature's geometry according to the specified coordinate transform.
QgsFeatureRequest mRequest
A copy of the feature request.
bool mClosed
Sets to true, as soon as the iterator is closed.
A vector of attributes.
Represents a coordinate reference system (CRS).
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Handles parsing and evaluation of expressions (formerly called "search strings").
QVariant evaluate()
Evaluate the feature and return the result.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
bool rewind()
Resets the iterator to the starting position.
Represents a list of OrderByClauses, with the most important first and the least important last.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the iterator to check if it should be ca...
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
long long limit() const
Returns the maximum number of features to request, or -1 if no limit set.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
Qgis::FeatureRequestFilterType filterType() const
Returns the attribute/ID filter type which is currently set on this request.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
const QgsFeatureIds & filterFids() const
Returns the feature IDs that should be fetched.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
QgsAttributes attributes
Definition qgsfeature.h:67
QgsFeatureId id
Definition qgsfeature.h:66
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
int attributeCount() const
Returns the number of attributes attached to the feature.
void padAttributes(int count)
Resizes the attributes attached to this feature by appending the specified count of NULL values to th...
void setId(QgsFeatureId id)
Sets the feature id for this feature.
QgsGeometry geometry
Definition qgsfeature.h:69
void setValid(bool validity)
Sets the validity of the feature.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
Container of fields for a vector layer.
Definition qgsfields.h:46
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:87
QString id
Definition qgsmaplayer.h:83
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
static QgsProject * instance()
Returns the QgsProject singleton instance.
Contains information about how to simplify geometries fetched from a QgsFeatureIterator.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Base class for vector data providers.
virtual QgsAbstractFeatureSource * featureSource() const =0
Returns feature source object that can be used for querying provider's data.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
QgsFeatureIds deletedFeatureIds() const
Returns a list of deleted feature IDs which are not committed.
QgsChangedAttributesMap changedAttributeValues() const
Returns a map of features with changed attributes values which are not committed.
QgsFeatureMap addedFeatures() const
Returns a map of new features which are not committed.
QgsGeometryMap changedGeometries() const
Returns a map of features with changed geometries which are not committed.
QList< QgsField > addedAttributes() const
Returns a list of added attributes fields which are not committed.
QgsAttributeList deletedAttributeIds() const
Returns a list of deleted attributes fields which are not committed.
void updateChangedAttributes(QgsFeature &f)
Update feature with uncommitted attribute updates.
void useAddedFeature(const QgsFeature &src, QgsFeature &f)
QMap< const QgsVectorLayerJoinInfo *, QgsVectorLayerFeatureIterator::FetchJoinInfo > mFetchJoinInfo
Information about joins used in the current select() statement.
QgsFeatureMap::ConstIterator mFetchAddedFeaturesIt
void setInterruptionChecker(QgsFeedback *interruptionChecker) override
Attach an object that can be queried regularly by the iterator to check if it must stopped.
void addVirtualAttributes(QgsFeature &f)
Adds attributes that don't source from the provider but are added inside QGIS Includes.
bool close() override
end of iterating: free the resources / lock
void updateFeatureGeometry(QgsFeature &f)
Update feature with uncommitted geometry updates.
void addExpressionAttribute(QgsFeature &f, int attrIndex)
Adds an expression based attribute to a feature.
QMap< int, QgsExpression * > mExpressionFieldInfo
QgsGeometryMap::ConstIterator mFetchChangedGeomIt
bool prepareSimplification(const QgsSimplifyMethod &simplifyMethod) override
Setup the simplification of geometries to fetch using the specified simplify method.
void useChangedAttributeFeature(QgsFeatureId fid, const QgsGeometry &geom, QgsFeature &f)
bool fetchFeature(QgsFeature &feature) override
fetch next feature, return true on success
std::shared_ptr< QgsGeometryEngine > mDistanceWithinEngine
bool isValid() const override
Returns if this iterator is valid.
bool rewind() override
reset the iterator to the starting position
QgsVectorLayerFeatureIterator(QgsVectorLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request)
Partial snapshot of vector layer's state (only the members necessary for access to features).
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system for features retrieved from this source.
QgsChangedAttributesMap mChangedAttributeValues
std::unique_ptr< QgsAbstractFeatureSource > mProviderFeatureSource
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) override
Gets an iterator for features matching the specified request.
QgsVectorLayerFeatureSource(const QgsVectorLayer *layer)
Constructor for QgsVectorLayerFeatureSource.
~QgsVectorLayerFeatureSource() override
QString id() const
Returns the layer id of the source layer.
QgsFields fields() const
Returns the fields that will be available for features that are retrieved from this source.
std::unique_ptr< QgsVectorLayerJoinBuffer > mJoinBuffer
std::unique_ptr< QgsExpressionFieldBuffer > mExpressionFieldBuffer
QMap< QString, JoinLayerSource > mJoinSources
Contains prepared join sources by layer ID.
bool containsJoins() const
Quick way to test if there is any join at all.
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer.
void createJoinCaches()
Calls cacheJoinLayer() for all vector joins.
static QVector< int > joinSubsetIndices(QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset)
Returns a vector of indices for use in join based on field names from the layer.
Defines left outer join from our vector layer to some other vector layer.
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer.
QString joinFieldName() const
Returns name of the field of joined layer that will be used for join.
QString targetFieldName() const
Returns name of the field of our layer that will be used for join.
QString joinLayerId() const
ID of the joined layer - may be used to resolve reference to the joined layer.
long long featureCount() const override
Returns the number of features contained in the source, or -1 if the feature count is unknown.
Qgis::WkbType wkbType() const override
Returns the geometry type for features returned by this source.
Qgis::SpatialIndexPresence hasSpatialIndex() const override
Returns an enum value representing the presence of a valid spatial index on the source,...
QgsFields fields() const override
Returns the fields associated with features in the source.
QgsCoordinateReferenceSystem sourceCrs() const override
Returns the coordinate reference system for features in the source.
QgsVectorLayerSelectedFeatureSource(QgsVectorLayer *layer)
Constructor for QgsVectorLayerSelectedFeatureSource, for selected features from the specified layer.
QgsExpressionContextScope * createExpressionContextScope() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QString sourceName() const override
Returns a friendly display name for the source.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Returns an iterator for the features in the source.
Represents a vector layer which manages a vector based dataset.
Q_INVOKABLE QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
QMap< int, QVariant > QgsAttributeMap
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
Definition qgsfield.h:28
QMap< QgsFeatureId, QgsFeature > QgsFeatureMap
Join information prepared for fast attribute id mapping in QgsVectorLayerJoinBuffer::updateFeatureAtt...
QMap< int, int > attributesSourceToDestLayerMap
Mapping from original attribute index to the joined layer index.
void addJoinedAttributesDirect(QgsFeature &f, const QVariant &joinValue) const
std::shared_ptr< QgsVectorLayerFeatureSource > joinSource
Feature source for join.
int targetField
Index of field (of this layer) that drives the join.
const QgsVectorLayerJoinInfo * joinInfo
Canonical source of information about the join.
int indexOffset
At what position the joined fields start.
void addJoinedAttributesCached(QgsFeature &f, const QVariant &joinValue) const
Contains join layer source information prepared in a thread-safe way, ready for vector layer feature ...
std::shared_ptr< QgsVectorLayerFeatureSource > joinSource
Feature source for join.