QGIS API Documentation 4.3.0-Master (52380286b7c)
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 QgsProject *project = layer->project();
73 // TODO QGIS 5.0 -- remove fallback to QgsProject.instance()
74 if ( !project )
75 {
76 project = QgsProject::instance(); // skip-keyword-check
77 }
78
80 mEllipsoid = project->ellipsoid();
81 mDistanceUnit = project->distanceUnits();
82 mAreaUnit = project->areaUnits();
83 mProjectScope = std::unique_ptr<QgsExpressionContextScope>( QgsExpressionContextUtils::projectScope( project ) );
84
85 mHasEditBuffer = layer->editBuffer();
86 if ( mHasEditBuffer )
87 {
88#if 0
89 // TODO[MD]: after merge
90 if ( request.filterType() == QgsFeatureRequest::FilterFid )
91 {
92
93 // only copy relevant parts
94 if ( L->editBuffer()->addedFeatures().contains( request.filterFid() ) )
95 mAddedFeatures.insert( request.filterFid(), L->editBuffer()->addedFeatures()[ request.filterFid()] );
96
97 if ( L->editBuffer()->changedGeometries().contains( request.filterFid() ) )
98 mChangedGeometries.insert( request.filterFid(), L->editBuffer()->changedGeometries()[ request.filterFid()] );
99
100 if ( L->editBuffer()->deletedFeatureIds().contains( request.filterFid() ) )
101 mDeletedFeatureIds.insert( request.filterFid() );
102
103 if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
104 mChangedAttributeValues.insert( request.filterFid(), L->editBuffer()->changedAttributeValues()[ request.filterFid()] );
105
106 if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
107 mChangedFeaturesRequest.setFilterFids( QgsFeatureIds() << request.filterFid() );
108 }
109 else
110 {
111#endif
112 // If we are inside a transaction the iterator "sees" the current status
113 if ( provider && !provider->transaction() )
114 {
119 mAddedAttributes = QList<QgsField>( layer->editBuffer()->addedAttributes() );
121 }
122#if 0
123 }
124#endif
125 }
126
127 const std::unique_ptr< QgsExpressionContextScope > layerScope( QgsExpressionContextUtils::layerScope( layer ) );
128 mLayerScope = *layerScope;
129}
130
132
134{
135 // return feature iterator that does not own this source
136 return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, false, request ) );
137}
138
143
148
150{
151 return mId;
152}
153
154
157{
158 mTransform = mRequest.calculateTransform( mSource->mCrs );
159 mHasValidTransform = mTransform.isValid();
160
161 // prepare spatial filter geometries for optimal speed
162 // since the mDistanceWithin* constraint member variables are all in the DESTINATION CRS,
163 // we set all these upfront before any transformation to the source CRS is done.
164
165 switch ( mRequest.spatialFilterType() )
166 {
167 case Qgis::SpatialFilterType::NoFilter:
168 case Qgis::SpatialFilterType::BoundingBox:
169 break;
170
171 case Qgis::SpatialFilterType::DistanceWithin:
172 if ( !mRequest.referenceGeometry().isEmpty() )
173 {
174 // Note that regardless of whether or not we'll ultimately be able to handoff this check to the underlying provider,
175 // we still need these reference geometry constraints in the vector layer iterator as we need them to check against
176 // the features from the vector layer's edit buffer! (In other words, we cannot completely hand off responsibility for
177 // these checks to the provider and ignore them locally)
178 mDistanceWithinGeom = mRequest.referenceGeometry();
179 mDistanceWithinEngine = mRequest.referenceGeometryEngine();
180 mDistanceWithinEngine->prepareGeometry();
181 mDistanceWithin = mRequest.distanceWithin();
182 }
183 break;
184 }
185
186 bool canDelegateLimitToProvider = true;
187 try
188 {
189 switch ( updateRequestToSourceCrs( mRequest, mTransform ) )
190 {
192 break;
193
195 // we have to disable any limit on the provider's request -- since that request may be returning features which are outside the
196 // distance tolerance, we'll have to fetch them all and then handle the limit check manually only after testing for the distance within constraint
197 canDelegateLimitToProvider = false;
198 break;
199 }
200
201 // mFilterRect is in the source CRS, so we set that now (after request transformation has been done)
202 mFilterRect = mRequest.filterRect();
203 }
204 catch ( QgsCsException & )
205 {
206 // can't reproject request filters
207 close();
208 return;
209 }
210
211 // check whether the order by clause(s) can be delegated to the provider
212 mDelegatedOrderByToProvider = !mSource->mHasEditBuffer;
213 if ( !mRequest.orderBy().isEmpty() )
214 {
215 QSet<int> attributeIndexes;
216 const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields );
217 for ( const int attrIndex : usedAttributeIndices )
218 {
219 if ( mSource->mFields.fieldOrigin( attrIndex ) != Qgis::FieldOrigin::Provider )
220 mDelegatedOrderByToProvider = false;
221
222 attributeIndexes << attrIndex;
223 }
224
225 if ( ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes ) && !mDelegatedOrderByToProvider )
226 {
227 attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
228 mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
229 }
230 }
231
232 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
233 {
234 mRequest.expressionContext()->setFields( mSource->mFields );
235 mRequest.filterExpression()->prepare( mRequest.expressionContext() );
236
237 if ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes )
238 {
239 // ensure that all fields required for filter expressions are prepared
240 QSet<int> attributeIndexes = mRequest.filterExpression()->referencedAttributeIndexes( mSource->mFields );
241 attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
242 mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
243 }
244 }
245
246 prepareFields();
247
248 mHasVirtualAttributes = !mFetchJoinInfo.isEmpty() || !mExpressionFieldInfo.isEmpty();
249
250 // by default provider's request is the same
251 mProviderRequest = mRequest;
252 // but we remove any destination CRS parameter - that is handled in QgsVectorLayerFeatureIterator,
253 // not at the provider level. Otherwise virtual fields depending on geometry would have incorrect
254 // values
255 if ( mRequest.coordinateTransform().isValid() || mRequest.destinationCrs().isValid() )
256 {
257 mProviderRequest.setCoordinateTransform( QgsCoordinateTransform() );
258 mProviderRequest.setDestinationCrs( QgsCoordinateReferenceSystem(), mRequest.transformContext() );
259 }
260
261 if ( !mDelegatedOrderByToProvider )
262 {
263 mProviderRequest.setOrderBy( QgsFeatureRequest::OrderBy() );
264 }
265
266 if ( !canDelegateLimitToProvider )
267 {
268 mProviderRequest.setLimit( -1 );
269 }
270
271 if ( mProviderRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes )
272 {
273 // prepare list of attributes to match provider fields
274 QSet<int> providerSubset;
275 const QgsAttributeList subset = mProviderRequest.subsetOfAttributes();
276 const int nPendingFields = mSource->mFields.count();
277 for ( const int attrIndex : subset )
278 {
279 if ( attrIndex < 0 || attrIndex >= nPendingFields )
280 continue;
281 if ( mSource->mFields.fieldOrigin( attrIndex ) == Qgis::FieldOrigin::Provider )
282 providerSubset << mSource->mFields.fieldOriginIndex( attrIndex );
283 }
284
285 // This is done in order to be prepared to do fallback order bys
286 // and be sure we have the required columns.
287 // TODO:
288 // It would be nicer to first check if we can compile the order by
289 // and only modify the subset if we cannot.
290 if ( !mProviderRequest.orderBy().isEmpty() )
291 {
292 const auto usedAttributeIndices = mProviderRequest.orderBy().usedAttributeIndices( mSource->mFields );
293 for ( const int attrIndex : usedAttributeIndices )
294 {
295 providerSubset << attrIndex;
296 }
297 }
298
299 mProviderRequest.setSubsetOfAttributes( qgis::setToList( providerSubset ) );
300 }
301
302 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
303 {
304 const bool needsGeom = mProviderRequest.filterExpression()->needsGeometry();
305 const auto constReferencedColumns = mProviderRequest.filterExpression()->referencedColumns();
306 for ( const QString &field : constReferencedColumns )
307 {
308 const int idx = source->mFields.lookupField( field );
309
310 // If there are fields in the expression which are not of origin provider, the provider will not be able to filter based on them.
311 // In this case we disable the expression filter.
312 if ( source->mFields.fieldOrigin( idx ) != Qgis::FieldOrigin::Provider )
313 {
314 mProviderRequest.disableFilter();
315 // can't limit at provider side
316 mProviderRequest.setLimit( -1 );
317 if ( needsGeom )
318 {
319 // have to get geometry from provider in order to evaluate expression on client
320 mProviderRequest.setFlags( mProviderRequest.flags() & ~( static_cast<int>( Qgis::FeatureRequestFlag::NoGeometry ) ) );
321 }
322 break;
323 }
324 }
325 }
326
327 if ( mSource->mHasEditBuffer )
328 {
329 mChangedFeaturesRequest = mProviderRequest;
330 QgsFeatureIds changedIds;
331 QgsChangedAttributesMap::const_iterator attIt = mSource->mChangedAttributeValues.constBegin();
332 for ( ; attIt != mSource->mChangedAttributeValues.constEnd(); ++attIt )
333 {
334 changedIds << attIt.key();
335 }
336 mChangedFeaturesRequest.setFilterFids( changedIds );
337
338 if ( mChangedFeaturesRequest.limit() > 0 )
339 {
340 int providerLimit = mProviderRequest.limit();
341
342 // features may be deleted in buffer, so increase limit sent to provider
343 providerLimit += mSource->mDeletedFeatureIds.size();
344
345 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression )
346 {
347 // attribute changes may mean some features no longer match expression, so increase limit sent to provider
348 providerLimit += mSource->mChangedAttributeValues.size();
349 }
350
351 if ( mProviderRequest.filterType() == Qgis::FeatureRequestFilterType::Expression || mProviderRequest.spatialFilterType() != Qgis::SpatialFilterType::NoFilter )
352 {
353 // geometry changes may mean some features no longer match expression or rect, so increase limit sent to provider
354 providerLimit += mSource->mChangedGeometries.size();
355 }
356
357 mProviderRequest.setLimit( providerLimit );
358 mChangedFeaturesRequest.setLimit( providerLimit );
359 }
360 }
361
362 if ( request.filterType() == Qgis::FeatureRequestFilterType::Fid )
363 {
364 mFetchedFid = false;
365 }
366 else // no filter or filter by rect
367 {
368 if ( mSource->mProviderFeatureSource )
369 {
370 if ( mSource->mHasEditBuffer )
371 {
372 mChangedFeaturesIterator = mSource->mProviderFeatureSource->getFeatures( mChangedFeaturesRequest );
373 }
374 else
375 {
376 mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
377 }
378 }
379
380 rewindEditBuffer();
381 }
382}
383
384
391
393
399class QgsThreadStackOverflowGuard
400{
401 public:
402#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
403 QgsThreadStackOverflowGuard( QThreadStorage<std::deque<QString>> &storage, const QString &stackFrameInformation, int maxDepth )
404#else
405 QgsThreadStackOverflowGuard( std::deque<QString> &storage, const QString &stackFrameInformation, int maxDepth )
406#endif
407 : mStorage( storage )
408 , mMaxDepth( maxDepth )
409 {
410#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
411 if ( !storage.hasLocalData() )
412 {
413 storage.setLocalData( std::deque<QString>() );
414 }
415
416 storage.localData().emplace_back( stackFrameInformation );
417#else
418 storage.emplace_back( stackFrameInformation );
419#endif
420 }
421
422 ~QgsThreadStackOverflowGuard()
423 {
424#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
425 mStorage.localData().pop_back();
426#else
427 mStorage.pop_back();
428#endif
429 }
430
431 bool hasStackOverflow() const
432 {
433#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
434 if ( mStorage.localData().size() > mMaxDepth )
435#else
436 if ( mStorage.size() > mMaxDepth )
437#endif
438 return true;
439 else
440 return false;
441 }
442
443 QString topFrames() const
444 {
445 QStringList dumpStack;
446#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
447 const std::deque<QString> &stack = mStorage.localData();
448#else
449 const std::deque<QString> &stack = mStorage;
450#endif
451
452 const int dumpSize = std::min( static_cast<int>( stack.size() ), 10 );
453 auto stackIt = stack.begin();
454 for ( int i = 0; i < dumpSize; ++i, stackIt++ )
455 {
456 dumpStack += *stackIt;
457 }
458
459 return dumpStack.join( '\n' );
460 }
461
462 std::size_t depth() const
463 {
464#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
465 return mStorage.localData().size();
466#else
467 return mStorage.size();
468#endif
469 }
470
471 private:
472#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
473 QThreadStorage<std::deque<QString>> &mStorage;
474#else
475 std::deque<QString> &mStorage;
476#endif
477 std::size_t mMaxDepth;
478};
479
481
483{
484 f.setValid( false );
485
486 if ( mClosed )
487 return false;
488
489#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
490 static QThreadStorage<std::deque<QString>> sStack;
491#else
492 static thread_local std::deque<QString> sStack;
493#endif
494
495 const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
496
497 if ( guard.hasStackOverflow() )
498 {
499 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 );
500 return false;
501 }
502
503 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
504 {
505 if ( mFetchedFid )
506 return false;
507 const bool res = nextFeatureFid( f );
508 if ( res && postProcessFeature( f ) )
509 {
510 mFetchedFid = true;
511 return res;
512 }
513 else
514 {
515 return false;
516 }
517 }
518
519 if ( !mFilterRect.isNull() )
520 {
522 return true;
523
524 // no more changed geometries
525 }
526
528 {
530 return true;
531
533 return true;
534
535 // no more changed features
536 }
537
538 while ( fetchNextAddedFeature( f ) )
539 {
540 return true;
541 }
542 // no more added features
543
544 if ( mProviderIterator.isClosed() )
545 {
547 if ( mSource->mProviderFeatureSource )
548 {
549 mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
550 mProviderIterator.setInterruptionChecker( mInterruptionChecker );
551 }
552 }
553
554 while ( mProviderIterator.nextFeature( f ) )
555 {
556 if ( mFetchConsidered.contains( f.id() ) )
557 continue;
558
559 // TODO[MD]: just one resize of attributes
560 f.setFields( mSource->mFields );
561
562 // update attributes
563 if ( mSource->mHasEditBuffer )
565
568 else
569 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
570
572 {
573 //filtering by expression, and couldn't do it on the provider side
574 mRequest.expressionContext()->setFeature( f );
575 if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
576 {
577 //feature did not match filter
578 continue;
579 }
580 }
581
582 // update geometry
583 // TODO[MK]: FilterRect check after updating the geometry
586
587 if ( !postProcessFeature( f ) )
588 continue;
589
590 return true;
591 }
592 // no more provider features
593
594 close();
595 return false;
596}
597
598
600{
601 if ( mClosed )
602 return false;
603
604 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
605 {
606 mFetchedFid = false;
607 }
608 else
609 {
610 mProviderIterator.rewind();
612 }
613
614 return true;
615}
616
618{
619 if ( mClosed )
620 return false;
621
622 mProviderIterator.close();
623
625
626 mClosed = true;
627 return true;
628}
629
631{
632 mProviderIterator.setInterruptionChecker( interruptionChecker );
633 mInterruptionChecker = interruptionChecker;
634}
635
637{
638 return mChangedFeaturesIterator.isValid() || mProviderIterator.isValid();
639}
640
642{
643 while ( mFetchAddedFeaturesIt != mSource->mAddedFeatures.constBegin() )
644 {
646 const QgsFeatureId fid = mFetchAddedFeaturesIt->id();
647
648 if ( mFetchConsidered.contains( fid ) )
649 // must have changed geometry outside rectangle
650 continue;
651
653
654 // can't test for feature acceptance until after calling useAddedFeature
655 // since acceptFeature may rely on virtual fields
656 if ( !mRequest.acceptFeature( f ) )
657 // skip features which are not accepted by the filter
658 continue;
659
660 if ( !postProcessFeature( f ) )
661 continue;
662
663 return true;
664 }
665
666 mFetchAddedFeaturesIt = mSource->mAddedFeatures.constBegin();
667 return false; // no more added features
668}
669
670
672{
673 // since QgsFeature is implicitly shared, it's more efficient to just copy the
674 // whole feature, even if flags like NoGeometry or a subset of attributes is set at the request.
675 // This helps potentially avoid an unnecessary detach of the feature
676 f = src;
677 f.setValid( true );
678 f.setFields( mSource->mFields );
679
682 else
683 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
684}
685
686
688{
689 // check if changed geometries are in rectangle
690 for ( ; mFetchChangedGeomIt != mSource->mChangedGeometries.constEnd(); mFetchChangedGeomIt++ )
691 {
692 const QgsFeatureId fid = mFetchChangedGeomIt.key();
693
694 if ( mFetchConsidered.contains( fid ) )
695 // skip deleted features
696 continue;
697
698 mFetchConsidered << fid;
699
700 if ( !mFilterRect.isNull() && !mFetchChangedGeomIt->intersects( mFilterRect ) )
701 // skip changed geometries not in rectangle and don't check again
702 continue;
703
705
707 {
708 mRequest.expressionContext()->setFeature( f );
709 if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
710 {
711 continue;
712 }
713 }
714
715 if ( postProcessFeature( f ) )
716 {
717 // return complete feature
719 return true;
720 }
721 }
722
723 return false; // no more changed geometries
724}
725
727{
728 while ( mChangedFeaturesIterator.nextFeature( f ) )
729 {
730 if ( mFetchConsidered.contains( f.id() ) )
731 continue;
732
733 mFetchConsidered << f.id();
734
736
737 // also update geometry if needed
738 const auto changedGeometryIt = mSource->mChangedGeometries.constFind( f.id() );
739 if ( changedGeometryIt != mSource->mChangedGeometries.constEnd() )
740 {
741 if ( !mFilterRect.isNull() && !changedGeometryIt->intersects( mFilterRect ) )
742 // skip changed geometries not in rectangle and don't check again
743 continue;
744
745 f.setGeometry( *changedGeometryIt );
746 }
747
750
751 mRequest.expressionContext()->setFeature( f );
752 if ( mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() && postProcessFeature( f ) )
753 {
754 return true;
755 }
756 }
757
758 return false;
759}
760
761
763{
764 f.setId( fid );
765 f.setValid( true );
766 f.setFields( mSource->mFields );
767
768 if ( !( mRequest.flags() & Qgis::FeatureRequestFlag::NoGeometry ) || ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Expression && mRequest.filterExpression()->needsGeometry() ) )
769 {
770 f.setGeometry( geom );
771 }
772
773 const bool subsetAttrs = ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes );
774 if ( !subsetAttrs || !mRequest.subsetOfAttributes().isEmpty() )
775 {
776 // retrieve attributes from provider
777 QgsFeature tmp;
778 //mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes );
779 QgsFeatureRequest request;
781 if ( subsetAttrs )
782 {
783 request.setSubsetOfAttributes( mProviderRequest.subsetOfAttributes() );
784 }
785 QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( request );
786 if ( fi.nextFeature( tmp ) )
787 {
788 if ( mHasVirtualAttributes || mSource->mHasEditBuffer )
790 f.setAttributes( tmp.attributes() );
791 }
792 }
793
795}
796
797
799{
800 mFetchConsidered = mSource->mDeletedFeatureIds;
801
802 mFetchAddedFeaturesIt = mSource->mAddedFeatures.constEnd();
803 mFetchChangedGeomIt = mSource->mChangedGeometries.constBegin();
804}
805
807{
808 if ( !mSource->mFields.exists( fieldIdx ) )
809 return;
810
811 if ( mSource->mFields.fieldOrigin( fieldIdx ) != Qgis::FieldOrigin::Join )
812 return;
813
814 int sourceLayerIndex;
815 const QgsVectorLayerJoinInfo *joinInfo = mSource->mJoinBuffer->joinForFieldIndex( fieldIdx, mSource->mFields, sourceLayerIndex );
816 Q_ASSERT( joinInfo );
817
818 auto joinSourceIt = mSource->mJoinSources.constFind( joinInfo->joinLayerId() );
819 if ( joinSourceIt == mSource->mJoinSources.constEnd() )
820 return; // invalid join (unresolved reference to layer)
821
822 if ( !mFetchJoinInfo.contains( joinInfo ) )
823 {
824 FetchJoinInfo info;
825 info.joinInfo = joinInfo;
826 info.joinSource = joinSourceIt->joinSource;
827 info.indexOffset = mSource->mJoinBuffer->joinedFieldsOffset( joinInfo, mSource->mFields );
828 info.targetField = mSource->mFields.indexFromName( joinInfo->targetFieldName() );
829 info.joinField = joinSourceIt->joinLayerFields.indexFromName( joinInfo->joinFieldName() );
830 info.joinLayerFields = joinSourceIt->joinLayerFields;
831
832 // for joined fields, we always need to request the targetField from the provider too
833 if ( !mPreparedFields.contains( info.targetField ) && !mFieldsToPrepare.contains( info.targetField ) )
834 mFieldsToPrepare << info.targetField;
835
836 if ( ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes ) && !mRequest.subsetOfAttributes().contains( info.targetField ) )
837 mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() << info.targetField );
838
839 mFetchJoinInfo.insert( joinInfo, info );
840 }
841
842 // store field source index - we'll need it when fetching from provider
843 mFetchJoinInfo[joinInfo].attributes.push_back( sourceLayerIndex );
844 mFetchJoinInfo[joinInfo].attributesSourceToDestLayerMap[sourceLayerIndex] = fieldIdx;
845}
846
847
849{
850#if !defined( USE_THREAD_LOCAL ) || defined( Q_OS_WIN )
851 static QThreadStorage<std::deque<QString>> sStack;
852#else
853 static thread_local std::deque<QString> sStack;
854#endif
855
856 const QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
857
858 if ( guard.hasStackOverflow() )
859 {
861 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 );
862 return;
863 }
864
865 const QList<QgsExpressionFieldBuffer::ExpressionField> &exps = mSource->mExpressionFieldBuffer->expressions();
866
867 const int oi = mSource->mFields.fieldOriginIndex( fieldIdx );
868 auto exp = std::make_unique<QgsExpression>( exps[oi].cachedExpression );
869
871 da.setSourceCrs( mSource->mCrs, mSource->mTransformContext );
872 da.setEllipsoid( mSource->mEllipsoid );
873 exp->setGeomCalculator( &da );
874 exp->setDistanceUnits( mSource->mDistanceUnit );
875 exp->setAreaUnits( mSource->mAreaUnit );
876
877 if ( !mExpressionContext )
878 createExpressionContext();
879 exp->prepare( mExpressionContext.get() );
880 const QSet<int> referencedColumns = exp->referencedAttributeIndexes( mSource->fields() );
881
882 QSet<int> requestedAttributes = qgis::listToSet( mRequest.subsetOfAttributes() );
883
884 for ( const int dependentFieldIdx : referencedColumns )
885 {
887 {
888 requestedAttributes += dependentFieldIdx;
889 }
890 // also need to fetch this dependent field
891 if ( !mPreparedFields.contains( dependentFieldIdx ) && !mFieldsToPrepare.contains( dependentFieldIdx ) )
892 mFieldsToPrepare << dependentFieldIdx;
893 }
894
896 {
897 mRequest.setSubsetOfAttributes( qgis::setToList( requestedAttributes ) );
898 }
899
900 if ( exp->needsGeometry() )
901 {
902 mRequest.setFlags( mRequest.flags() & ~( static_cast<int>( Qgis::FeatureRequestFlag::NoGeometry ) ) );
903 }
904
905 mExpressionFieldInfo.insert( fieldIdx, exp.release() );
906}
907
909{
910 mPreparedFields.clear();
911 mFieldsToPrepare.clear();
912 mFetchJoinInfo.clear();
913 mOrderedJoinInfoList.clear();
914
915 mExpressionContext.reset();
916
917 mFieldsToPrepare = ( mRequest.flags() & Qgis::FeatureRequestFlag::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList();
918
919 while ( !mFieldsToPrepare.isEmpty() )
920 {
921 const int fieldIdx = mFieldsToPrepare.takeFirst();
922 if ( mPreparedFields.contains( fieldIdx ) )
923 continue;
924
925 mPreparedFields << fieldIdx;
926 prepareField( fieldIdx );
927 }
928
929 //sort joins by dependency
930 if ( !mFetchJoinInfo.empty() )
931 {
932 createOrderedJoinList();
933 }
934}
935
936void QgsVectorLayerFeatureIterator::createOrderedJoinList()
937{
938 mOrderedJoinInfoList = mFetchJoinInfo.values();
939 if ( mOrderedJoinInfoList.size() < 2 )
940 {
941 return;
942 }
943
944 QSet<int> resolvedFields; //todo: get provider / virtual fields without joins
945
946 //add all provider fields without joins as resolved fields
947 QList< int >::const_iterator prepFieldIt = mPreparedFields.constBegin();
948 for ( ; prepFieldIt != mPreparedFields.constEnd(); ++prepFieldIt )
949 {
950 if ( mSource->mFields.fieldOrigin( *prepFieldIt ) != Qgis::FieldOrigin::Join )
951 {
952 resolvedFields.insert( *prepFieldIt );
953 }
954 }
955
956 //iterate through the joins. If target field is not yet covered, move the entry to the end of the list
957
958 //some join combinations might not have a resolution at all
959 const int maxIterations = ( mOrderedJoinInfoList.size() + 1 ) * mOrderedJoinInfoList.size() / 2.0;
960 int currentIteration = 0;
961
962 for ( int i = 0; i < mOrderedJoinInfoList.size() - 1; ++i )
963 {
964 if ( !resolvedFields.contains( mOrderedJoinInfoList.at( i ).targetField ) )
965 {
966 mOrderedJoinInfoList.append( mOrderedJoinInfoList.at( i ) );
967 mOrderedJoinInfoList.removeAt( i );
968 --i;
969 }
970 else
971 {
972 const int offset = mOrderedJoinInfoList.at( i ).indexOffset;
973 const int joinField = mOrderedJoinInfoList.at( i ).joinField;
974
975 const QgsAttributeList attributes = mOrderedJoinInfoList.at( i ).attributes;
976 for ( int n = 0; n < attributes.size(); n++ )
977 {
978 if ( n != joinField )
979 {
980 resolvedFields.insert( joinField < n ? n + offset - 1 : n + offset );
981 }
982 }
983 }
984
985 ++currentIteration;
986 if ( currentIteration >= maxIterations )
987 {
988 break;
989 }
990 }
991}
992
993bool QgsVectorLayerFeatureIterator::postProcessFeature( QgsFeature &feature )
994{
995 bool result = checkGeometryValidity( feature );
996 if ( result && mHasValidTransform )
998
999 if ( result && mDistanceWithinEngine && feature.hasGeometry() )
1000 {
1001 result = mDistanceWithinEngine->distanceWithin( feature.geometry().constGet(), mDistanceWithin );
1002 }
1003
1004 return result;
1005}
1006
1007bool QgsVectorLayerFeatureIterator::checkGeometryValidity( const QgsFeature &feature )
1008{
1009 switch ( mRequest.invalidGeometryCheck() )
1010 {
1012 return true;
1013
1015 {
1016 if ( !feature.hasGeometry() )
1017 return true;
1018
1019 if ( !feature.geometry().isGeosValid() )
1020 {
1021 QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
1022 if ( mRequest.invalidGeometryCallback() )
1023 {
1024 mRequest.invalidGeometryCallback()( feature );
1025 }
1026 return false;
1027 }
1028 break;
1029 }
1030
1032 if ( !feature.hasGeometry() )
1033 return true;
1034
1035 if ( !feature.geometry().isGeosValid() )
1036 {
1037 QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::MessageLevel::Critical );
1038 close();
1039 if ( mRequest.invalidGeometryCallback() )
1040 {
1041 mRequest.invalidGeometryCallback()( feature );
1042 }
1043 return false;
1044 }
1045 break;
1046 }
1047
1048 return true;
1049}
1050
1052{
1053 switch ( mSource->mFields.fieldOrigin( fieldIdx ) )
1054 {
1056 prepareExpression( fieldIdx );
1057 break;
1058
1060 if ( mSource->mJoinBuffer->containsJoins() )
1061 {
1062 prepareJoin( fieldIdx );
1063 }
1064 break;
1065
1069 break;
1070 }
1071}
1072
1074{
1075 QList< FetchJoinInfo >::const_iterator joinIt = mOrderedJoinInfoList.constBegin();
1076 for ( ; joinIt != mOrderedJoinInfoList.constEnd(); ++joinIt )
1077 {
1078 const QVariant targetFieldValue = f.attribute( joinIt->targetField );
1079 if ( !targetFieldValue.isValid() )
1080 continue;
1081
1082 const QHash< QString, QgsAttributes> &memoryCache = joinIt->joinInfo->cachedAttributes;
1083 if ( memoryCache.isEmpty() )
1084 {
1085 if ( joinIt->joinInfo->isUsingMemoryCache() && !joinIt->joinInfo->cacheDirty )
1086 continue;
1087
1088 joinIt->addJoinedAttributesDirect( f, targetFieldValue );
1089 }
1090 else
1091 joinIt->addJoinedAttributesCached( f, targetFieldValue );
1092 }
1093}
1094
1096{
1097 // make sure we have space for newly added attributes
1098 QgsAttributes attr = f.attributes();
1099 attr.resize( mSource->mFields.count() ); // Provider attrs count + joined attrs count + expression attrs count
1100 f.setAttributes( attr );
1101
1102 // possible TODO - handle combinations of expression -> join -> expression -> join?
1103 // but for now, write that off as too complex and an unlikely rare, unsupported use case
1104
1105 QList< int > fetchedVirtualAttributes;
1106 //first, check through joins for any virtual fields we need
1107 QMap<const QgsVectorLayerJoinInfo *, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
1108 for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
1109 {
1110 if ( mExpressionFieldInfo.contains( joinIt->targetField ) )
1111 {
1112 // have to calculate expression field before we can handle this join
1113 addExpressionAttribute( f, joinIt->targetField );
1114 fetchedVirtualAttributes << joinIt->targetField;
1115 }
1116 }
1117
1118 if ( !mFetchJoinInfo.isEmpty() )
1120
1121 // add remaining expression fields
1122 if ( !mExpressionFieldInfo.isEmpty() )
1123 {
1124 QMap<int, QgsExpression *>::ConstIterator it = mExpressionFieldInfo.constBegin();
1125 for ( ; it != mExpressionFieldInfo.constEnd(); ++it )
1126 {
1127 if ( fetchedVirtualAttributes.contains( it.key() ) )
1128 continue;
1129
1130 addExpressionAttribute( f, it.key() );
1131 }
1132 }
1133}
1134
1136{
1137 QgsExpression *exp = mExpressionFieldInfo.value( attrIndex );
1138 if ( exp )
1139 {
1140 if ( !mExpressionContext )
1141 createExpressionContext();
1142
1143 mExpressionContext->setFeature( f );
1144 QVariant val = exp->evaluate( mExpressionContext.get() );
1145 ( void ) mSource->mFields.at( attrIndex ).convertCompatible( val );
1146 f.setAttribute( attrIndex, val );
1147 }
1148 else
1149 {
1150 f.setAttribute( attrIndex, QVariant() );
1151 }
1152}
1153
1155{
1156 Q_UNUSED( simplifyMethod )
1157 return false;
1158}
1159
1160bool QgsVectorLayerFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
1161{
1162 Q_UNUSED( methodType )
1163 return false;
1164}
1165
1166
1168{
1169 const QHash<QString, QgsAttributes> &memoryCache = joinInfo->cachedAttributes;
1170 const QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
1171 if ( it == memoryCache.constEnd() )
1172 return; // joined value not found -> leaving the attributes empty (null)
1173
1174 int index = indexOffset;
1175
1176 const QgsAttributes &featureAttributes = it.value();
1177 for ( int i = 0; i < featureAttributes.count(); ++i )
1178 {
1179 f.setAttribute( index++, featureAttributes.at( i ) );
1180 }
1181}
1182
1183
1185{
1186#if 0 // this is not thread safe -- we cannot access the layer here as this will be called from non-main threads.
1187 // Shortcut
1188 if ( joinLayer && ! joinLayer->hasFeatures() )
1189 {
1190 return;
1191 }
1192#endif
1193
1194 // no memory cache, query the joined values by setting substring
1195 QString subsetString;
1196
1197 const QString joinFieldName = joinInfo->joinFieldName();
1198
1199 subsetString.append( u"\"%1\""_s.arg( joinFieldName ) );
1200
1201 if ( QgsVariantUtils::isNull( joinValue ) )
1202 {
1203 subsetString += " IS NULL"_L1;
1204 }
1205 else
1206 {
1207 QString v = joinValue.toString();
1208 switch ( joinValue.userType() )
1209 {
1210 case QMetaType::Type::Int:
1211 case QMetaType::Type::LongLong:
1212 case QMetaType::Type::Double:
1213 break;
1214
1215 default:
1216 case QMetaType::Type::QString:
1217 v.replace( '\'', "''"_L1 );
1218 v.prepend( '\'' ).append( '\'' );
1219 break;
1220 }
1221 subsetString += '=' + v;
1222 }
1223
1224 QList<int> joinedAttributeIndices;
1225
1226 // maybe user requested just a subset of layer's attributes
1227 // so we do not have to cache everything
1228 if ( joinInfo->hasSubset() )
1229 {
1230 const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinInfo, joinLayerFields );
1231 const QVector<int> subsetIndices = QgsVectorLayerJoinBuffer::joinSubsetIndices( joinLayerFields, subsetNames );
1232 joinedAttributeIndices = qgis::setToList( qgis::listToSet( attributes ).intersect( qgis::listToSet( subsetIndices.toList() ) ) );
1233 }
1234 else
1235 {
1236 joinedAttributeIndices = attributes;
1237 }
1238
1239 // we don't need the join field, it is already present in the other table
1240 joinedAttributeIndices.removeAll( joinField );
1241
1242 // select (no geometry)
1243 QgsFeatureRequest request;
1245 request.setSubsetOfAttributes( joinedAttributeIndices );
1246 request.setFilterExpression( subsetString );
1247 request.setLimit( 1 );
1248 request.setRequestMayBeNested( true );
1249 QgsFeatureIterator fi = joinSource->getFeatures( request );
1250
1251 // get first feature
1252 const QList<int> sourceAttrIndexes = attributesSourceToDestLayerMap.keys();
1253 QgsFeature fet;
1254 if ( fi.nextFeature( fet ) )
1255 {
1256 const QgsAttributes attr = fet.attributes();
1257
1258 for ( const int sourceAttrIndex : sourceAttrIndexes )
1259 {
1260 if ( sourceAttrIndex == joinField )
1261 continue;
1262
1263 const int destAttrIndex = attributesSourceToDestLayerMap.value( sourceAttrIndex );
1264
1265 f.setAttribute( destAttrIndex, attr.at( sourceAttrIndex ) );
1266 }
1267 }
1268 else
1269 {
1270 // no suitable join feature found, keeping empty (null) attributes
1271 }
1272}
1273
1274
1276{
1277 const QgsFeatureId featureId = mRequest.filterFid();
1278
1279 // deleted already?
1280 if ( mSource->mDeletedFeatureIds.contains( featureId ) )
1281 return false;
1282
1283 // has changed geometry?
1284 if ( !( mRequest.flags() & Qgis::FeatureRequestFlag::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) )
1285 {
1286 useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f );
1287 return true;
1288 }
1289
1290 // added features
1291 for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter )
1292 {
1293 if ( iter->id() == featureId )
1294 {
1295 useAddedFeature( *iter, f );
1296 return true;
1297 }
1298 }
1299
1300 // regular features
1301 QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
1302 if ( fi.nextFeature( f ) )
1303 {
1304 f.setFields( mSource->mFields );
1305
1306 if ( mSource->mHasEditBuffer )
1308
1311 else
1312 f.padAttributes( mSource->mFields.count() - f.attributeCount() );
1313
1314 return true;
1315 }
1316
1317 return false;
1318}
1319
1321{
1322 QgsAttributes attrs = f.attributes();
1323
1324 // remove all attributes that will disappear - from higher indices to lower
1325 for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
1326 {
1327 attrs.remove( mSource->mDeletedAttributeIds[idx] );
1328 }
1329
1330 // adjust size to accommodate added attributes
1331 attrs.resize( attrs.count() + mSource->mAddedAttributes.count() );
1332
1333 // update changed attributes
1334 if ( mSource->mChangedAttributeValues.contains( f.id() ) )
1335 {
1336 const QgsAttributeMap &map = mSource->mChangedAttributeValues[f.id()];
1337 for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
1338 attrs[it.key()] = it.value();
1339 }
1340 f.setAttributes( attrs );
1341}
1342
1344{
1345 if ( mSource->mChangedGeometries.contains( f.id() ) )
1346 f.setGeometry( mSource->mChangedGeometries[f.id()] );
1347}
1348
1349void QgsVectorLayerFeatureIterator::createExpressionContext()
1350{
1351 mExpressionContext = std::make_unique< QgsExpressionContext >();
1352 mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
1353 mExpressionContext->appendScope( mSource->mProjectScope.release() );
1354 mExpressionContext->appendScope( new QgsExpressionContextScope( mSource->mLayerScope ) );
1355 mExpressionContext->setFeedback( mRequest.feedback() );
1356}
1357
1358bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys )
1359{
1360 Q_UNUSED( orderBys )
1361 return mDelegatedOrderByToProvider;
1362}
1363
1364
1365//
1366// QgsVectorLayerSelectedFeatureSource
1367//
1368
1370 : mSource( layer )
1371 , mSelectedFeatureIds( layer->selectedFeatureIds() )
1372 , mWkbType( layer->wkbType() )
1373 , mName( layer->name() )
1374 , mLayer( layer )
1375{}
1376
1378{
1379 QgsFeatureRequest req( request );
1380
1381 // while QgsVectorLayerSelectedFeatureIterator will reject any features not in mSelectedFeatureIds,
1382 // we still tweak the feature request to only request selected feature ids wherever we can -- this
1383 // allows providers to optimise the request and avoid requesting features we don't need
1384 // note that we can't do this for some request types - e.g. expression based requests, so
1385 // in that case we just pass the request on to the provider and let QgsVectorLayerSelectedFeatureIterator
1386 // do ALL the filtering
1387 if ( req.filterFids().isEmpty() && req.filterType() == Qgis::FeatureRequestFilterType::NoFilter )
1388 {
1389 req.setFilterFids( mSelectedFeatureIds );
1390 }
1391 else if ( !req.filterFids().isEmpty() )
1392 {
1393 QgsFeatureIds reqIds = mSelectedFeatureIds;
1394 reqIds.intersect( req.filterFids() );
1395 req.setFilterFids( reqIds );
1396 }
1397
1398 return QgsFeatureIterator( new QgsVectorLayerSelectedFeatureIterator( mSelectedFeatureIds, req, mSource ) );
1399}
1400
1405
1407{
1408 return mSource.fields();
1409}
1410
1412{
1413 return mWkbType;
1414}
1415
1417{
1418 return mSelectedFeatureIds.count();
1419}
1420
1422{
1423 return mName;
1424}
1425
1427{
1428 if ( mLayer )
1429 return mLayer->createExpressionContextScope();
1430 else
1431 return nullptr;
1432}
1433
1435{
1436 if ( mLayer )
1437 return mLayer->hasSpatialIndex();
1438 else
1440}
1441
1442//
1443// QgsVectorLayerSelectedFeatureIterator
1444//
1445
1447QgsVectorLayerSelectedFeatureIterator::QgsVectorLayerSelectedFeatureIterator( const QgsFeatureIds &selectedFeatureIds, const QgsFeatureRequest &request, QgsVectorLayerFeatureSource &source )
1448 : QgsAbstractFeatureIterator( request )
1449 , mSelectedFeatureIds( selectedFeatureIds )
1450{
1451 QgsFeatureRequest sourceRequest = request;
1452 if ( sourceRequest.filterType() == Qgis::FeatureRequestFilterType::Expression && sourceRequest.limit() > 0 )
1453 {
1454 // we can't pass the request limit to the provider here - otherwise the provider will
1455 // limit the number of returned features and may only return a bunch of matching features
1456 // which AREN'T in the selected feature set
1457 sourceRequest.setLimit( -1 );
1458 }
1459 mIterator = source.getFeatures( sourceRequest );
1460}
1461
1462bool QgsVectorLayerSelectedFeatureIterator::rewind()
1463{
1464 return mIterator.rewind();
1465}
1466
1467bool QgsVectorLayerSelectedFeatureIterator::close()
1468{
1469 return mIterator.close();
1470}
1471
1472bool QgsVectorLayerSelectedFeatureIterator::fetchFeature( QgsFeature &f )
1473{
1474 while ( mIterator.nextFeature( f ) )
1475 {
1476 if ( mSelectedFeatureIds.contains( f.id() ) )
1477 return true;
1478 }
1479 return false;
1480}
1481
@ Fid
Filter using feature ID.
Definition qgis.h:2409
@ Expression
Filter using expression.
Definition qgis.h:2410
@ NoFilter
No filter is applied.
Definition qgis.h:2408
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:619
@ Unknown
Spatial index presence cannot be determined, index may or may not exist.
Definition qgis.h:620
@ SubsetOfAttributes
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag).
Definition qgis.h:2381
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2380
@ Critical
Critical/error message.
Definition qgis.h:163
@ NoFilter
No spatial filtering of features.
Definition qgis.h:2437
@ Provider
Field originates from the underlying data provider of the vector layer.
Definition qgis.h:1876
@ Edit
Field has been temporarily added in editing mode.
Definition qgis.h:1878
@ Unknown
The field origin has not been specified.
Definition qgis.h:1875
@ Expression
Field is calculated from an expression.
Definition qgis.h:1879
@ Join
Field originates from a joined layer.
Definition qgis.h:1877
@ NoCheck
No invalid geometry checking.
Definition qgis.h:2424
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Definition qgis.h:2426
@ SkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
Definition qgis.h:2425
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:314
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 std::unique_ptr< QgsExpressionContextScope > globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
static std::unique_ptr< QgsExpressionContextScope > layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static std::unique_ptr< QgsExpressionContextScope > projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
Handles parsing and evaluation of expressions (formerly called "search strings").
QVariant evaluate()
Evaluate the feature and return the result.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
bool rewind()
Resets the iterator to the starting position.
Represents a list of OrderByClauses, with the most important first and the least important last.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the iterator to check if it should be ca...
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
long long limit() const
Returns the maximum number of features to request, or -1 if no limit set.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
Qgis::FeatureRequestFilterType filterType() const
Returns the attribute/ID filter type which is currently set on this request.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
const QgsFeatureIds & filterFids() const
Returns the feature IDs that should be fetched.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
QgsAttributes attributes
Definition qgsfeature.h:64
QgsFeatureId id
Definition qgsfeature.h:63
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
int attributeCount() const
Returns the number of attributes attached to the feature.
void padAttributes(int count)
Resizes the attributes attached to this feature by appending the specified count of NULL values to th...
void setId(QgsFeatureId id)
Sets the feature id for this feature.
QgsGeometry geometry
Definition qgsfeature.h:66
void setValid(bool validity)
Sets the validity of the feature.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
Container of fields for a vector layer.
Definition qgsfields.h:45
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
QString id
Definition qgsmaplayer.h:86
QgsProject * project() const
Returns the parent project if this map layer is added to a project.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
Qgis::DistanceUnit distanceUnits
Definition qgsproject.h:132
static QgsProject * instance()
Returns the QgsProject singleton instance.
QString ellipsoid
Definition qgsproject.h:122
Qgis::AreaUnit areaUnits
Definition qgsproject.h:133
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:121
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.
QgsCoordinateTransformContext mTransformContext
QgsFields fields() const
Returns the fields that will be available for features that are retrieved from this source.
std::unique_ptr< QgsExpressionContextScope > mProjectScope
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.