QGIS API Documentation  2.99.0-Master (9f5e33a)
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  ***************************************************************************/
16 
18 #include "qgsgeometrysimplifier.h"
19 #include "qgssimplifymethod.h"
20 #include "qgsvectordataprovider.h"
22 #include "qgsvectorlayer.h"
24 #include "qgsexpressioncontext.h"
25 #include "qgsdistancearea.h"
26 #include "qgsproject.h"
27 #include "qgsmessagelog.h"
28 #include "qgsexception.h"
29 
31 {
32  QMutexLocker locker( &layer->mFeatureSourceConstructorMutex );
34  mFields = layer->fields();
35 
36  // update layer's join caches if necessary
37  if ( layer->mJoinBuffer->containsJoins() )
38  layer->mJoinBuffer->createJoinCaches();
39 
40  mJoinBuffer = layer->mJoinBuffer->clone();
41 
42  mExpressionFieldBuffer = new QgsExpressionFieldBuffer( *layer->mExpressionFieldBuffer );
43  mCrs = layer->crs();
44 
45  mHasEditBuffer = layer->editBuffer();
46  if ( mHasEditBuffer )
47  {
48 #if 0
49  // TODO[MD]: after merge
50  if ( request.filterType() == QgsFeatureRequest::FilterFid )
51  {
52 
53  // only copy relevant parts
54  if ( L->editBuffer()->addedFeatures().contains( request.filterFid() ) )
55  mAddedFeatures.insert( request.filterFid(), L->editBuffer()->addedFeatures()[ request.filterFid()] );
56 
57  if ( L->editBuffer()->changedGeometries().contains( request.filterFid() ) )
58  mChangedGeometries.insert( request.filterFid(), L->editBuffer()->changedGeometries()[ request.filterFid()] );
59 
60  if ( L->editBuffer()->deletedFeatureIds().contains( request.filterFid() ) )
61  mDeletedFeatureIds.insert( request.filterFid() );
62 
63  if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
64  mChangedAttributeValues.insert( request.filterFid(), L->editBuffer()->changedAttributeValues()[ request.filterFid()] );
65 
66  if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
67  mChangedFeaturesRequest.setFilterFids( QgsFeatureIds() << request.filterFid() );
68  }
69  else
70  {
71 #endif
76  mAddedAttributes = QList<QgsField>( layer->editBuffer()->addedAttributes() );
78 #if 0
79  }
80 #endif
81  }
82 }
83 
85 {
86  delete mJoinBuffer;
89 }
90 
92 {
93  // return feature iterator that does not own this source
94  return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, false, request ) );
95 }
96 
98 {
99  return mFields;
100 }
101 
103 {
104  return mCrs;
105 }
106 
107 
110  , mFetchedFid( false )
111  , mInterruptionChecker( nullptr )
112 {
114  {
116  }
117  try
118  {
120  }
121  catch ( QgsCsException & )
122  {
123  // can't reproject mFilterRect
124  mClosed = true;
125  return;
126  }
127  if ( !mFilterRect.isNull() )
128  {
129  // update request to be the unprojected filter rect
131  }
132 
134  {
137 
139  {
140  //ensure that all fields required for filter expressions are prepared
141  QSet<int> attributeIndexes = mRequest.filterExpression()->referencedAttributeIndexes( mSource->mFields );
142  attributeIndexes += mRequest.subsetOfAttributes().toSet();
143  mRequest.setSubsetOfAttributes( attributeIndexes.toList() );
144  }
145  }
146 
147  prepareFields();
148 
149  mHasVirtualAttributes = !mFetchJoinInfo.isEmpty() || !mExpressionFieldInfo.isEmpty();
150 
151  // by default provider's request is the same
153  // but we remove any destination CRS parameter - that is handled in QgsVectorLayerFeatureIterator,
154  // not at the provider level. Otherwise virtual fields depending on geometry would have incorrect
155  // values
156  if ( mRequest.destinationCrs().isValid() )
157  {
159  }
160 
162  {
163  // prepare list of attributes to match provider fields
164  QSet<int> providerSubset;
166  int nPendingFields = mSource->mFields.count();
167  Q_FOREACH ( int attrIndex, subset )
168  {
169  if ( attrIndex < 0 || attrIndex >= nPendingFields )
170  continue;
171  if ( mSource->mFields.fieldOrigin( attrIndex ) == QgsFields::OriginProvider )
172  providerSubset << mSource->mFields.fieldOriginIndex( attrIndex );
173  }
174 
175  // This is done in order to be prepared to do fallback order bys
176  // and be sure we have the required columns.
177  // TODO:
178  // It would be nicer to first check if we can compile the order by
179  // and only modify the subset if we cannot.
180  if ( !mProviderRequest.orderBy().isEmpty() )
181  {
182  Q_FOREACH ( const QString &attr, mProviderRequest.orderBy().usedAttributes() )
183  {
184  providerSubset << mSource->mFields.lookupField( attr );
185  }
186  }
187 
188  mProviderRequest.setSubsetOfAttributes( providerSubset.toList() );
189  }
190 
192  {
193  Q_FOREACH ( const QString &field, mProviderRequest.filterExpression()->referencedColumns() )
194  {
195  int idx = source->mFields.lookupField( field );
196 
197  // If there are fields in the expression which are not of origin provider, the provider will not be able to filter based on them.
198  // In this case we disable the expression filter.
199  if ( source->mFields.fieldOrigin( idx ) != QgsFields::OriginProvider )
200  {
202  // can't limit at provider side
204  }
205  }
206  }
207 
208  if ( mSource->mHasEditBuffer )
209  {
211  QgsFeatureIds changedIds;
212  QgsChangedAttributesMap::const_iterator attIt = mSource->mChangedAttributeValues.constBegin();
213  for ( ; attIt != mSource->mChangedAttributeValues.constEnd(); ++attIt )
214  {
215  changedIds << attIt.key();
216  }
218 
219  if ( mChangedFeaturesRequest.limit() > 0 )
220  {
221  int providerLimit = mProviderRequest.limit();
222 
223  // features may be deleted in buffer, so increase limit sent to provider
224  providerLimit += mSource->mDeletedFeatureIds.size();
225 
227  {
228  // attribute changes may mean some features no longer match expression, so increase limit sent to provider
229  providerLimit += mSource->mChangedAttributeValues.size();
230  }
231 
233  {
234  // geometry changes may mean some features no longer match expression or rect, so increase limit sent to provider
235  providerLimit += mSource->mChangedGeometries.size();
236  }
237 
238  mProviderRequest.setLimit( providerLimit );
239  mChangedFeaturesRequest.setLimit( providerLimit );
240  }
241  }
242 
243  if ( request.filterType() == QgsFeatureRequest::FilterFid )
244  {
245  mFetchedFid = false;
246  }
247  else // no filter or filter by rect
248  {
249  if ( mSource->mHasEditBuffer )
250  {
252  }
253  else
254  {
256  }
257 
259  }
260 }
261 
262 
264 {
265  qDeleteAll( mExpressionFieldInfo );
266 
267  close();
268 }
269 
270 
271 
273 {
274  f.setValid( false );
275 
276  if ( mClosed )
277  return false;
278 
280  {
281  if ( mFetchedFid )
282  return false;
283  bool res = nextFeatureFid( f );
284  if ( res && postProcessFeature( f ) )
285  {
286  mFetchedFid = true;
287  return res;
288  }
289  else
290  {
291  return false;
292  }
293  }
294 
295  if ( !mFilterRect.isNull() )
296  {
297  if ( fetchNextChangedGeomFeature( f ) )
298  return true;
299 
300  // no more changed geometries
301  }
302 
304  {
306  return true;
307 
308  if ( fetchNextChangedGeomFeature( f ) )
309  return true;
310 
311  // no more changed features
312  }
313 
314  while ( fetchNextAddedFeature( f ) )
315  {
316  return true;
317  }
318  // no more added features
319 
320  if ( mProviderIterator.isClosed() )
321  {
324  mProviderIterator.setInterruptionChecker( mInterruptionChecker );
325  }
326 
327  while ( mProviderIterator.nextFeature( f ) )
328  {
329  if ( mFetchConsidered.contains( f.id() ) )
330  continue;
331 
332  // TODO[MD]: just one resize of attributes
333  f.setFields( mSource->mFields );
334 
335  // update attributes
336  if ( mSource->mHasEditBuffer )
338 
339  if ( mHasVirtualAttributes )
341 
343  {
344  //filtering by expression, and couldn't do it on the provider side
346  if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
347  {
348  //feature did not match filter
349  continue;
350  }
351  }
352 
353  // update geometry
354  // TODO[MK]: FilterRect check after updating the geometry
357 
358  if ( !postProcessFeature( f ) )
359  continue;
360 
361  return true;
362  }
363  // no more provider features
364 
365  close();
366  return false;
367 }
368 
369 
370 
372 {
373  if ( mClosed )
374  return false;
375 
377  {
378  mFetchedFid = false;
379  }
380  else
381  {
384  }
385 
386  return true;
387 }
388 
390 {
391  if ( mClosed )
392  return false;
393 
395 
396  iteratorClosed();
397 
398  mClosed = true;
399  return true;
400 }
401 
403 {
404  mProviderIterator.setInterruptionChecker( interruptionChecker );
405  mInterruptionChecker = interruptionChecker;
406 }
407 
409 {
410  while ( mFetchAddedFeaturesIt-- != mSource->mAddedFeatures.constBegin() )
411  {
413 
414  if ( mFetchConsidered.contains( fid ) )
415  // must have changed geometry outside rectangle
416  continue;
417 
419 
420  // can't test for feature acceptance until after calling useAddedFeature
421  // since acceptFeature may rely on virtual fields
422  if ( !mRequest.acceptFeature( f ) )
423  // skip features which are not accepted by the filter
424  continue;
425 
426  if ( !postProcessFeature( f ) )
427  continue;
428 
429  return true;
430  }
431 
433  return false; // no more added features
434 }
435 
436 
438 {
439  // since QgsFeature is implicitly shared, it's more efficient to just copy the
440  // whole feature, even if flags like NoGeometry or a subset of attributes is set at the request.
441  // This helps potentially avoid an unnecessary detach of the feature
442  f = src;
443  f.setValid( true );
444  f.setFields( mSource->mFields );
445 
446  if ( mHasVirtualAttributes )
448 }
449 
450 
451 
453 {
454  // check if changed geometries are in rectangle
456  {
457  QgsFeatureId fid = mFetchChangedGeomIt.key();
458 
459  if ( mFetchConsidered.contains( fid ) )
460  // skip deleted features
461  continue;
462 
463  mFetchConsidered << fid;
464 
465  if ( !mFilterRect.isNull() && !mFetchChangedGeomIt->intersects( mFilterRect ) )
466  // skip changed geometries not in rectangle and don't check again
467  continue;
468 
470 
472  {
474  if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
475  {
476  continue;
477  }
478  }
479 
480  if ( postProcessFeature( f ) )
481  {
482  // return complete feature
484  return true;
485  }
486  }
487 
488  return false; // no more changed geometries
489 }
490 
492 {
494  {
495  if ( mFetchConsidered.contains( f.id() ) )
496  // skip deleted features and those already handled by the geometry
497  continue;
498 
499  mFetchConsidered << f.id();
500 
502 
503  if ( mHasVirtualAttributes )
505 
507  if ( mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() && postProcessFeature( f ) )
508  {
509  return true;
510  }
511  }
512 
513  return false;
514 }
515 
516 
518 {
519  f.setId( fid );
520  f.setValid( true );
521  f.setFields( mSource->mFields );
522 
525  {
526  f.setGeometry( geom );
527  }
528 
529  bool subsetAttrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes );
530  if ( !subsetAttrs || !mRequest.subsetOfAttributes().isEmpty() )
531  {
532  // retrieve attributes from provider
533  QgsFeature tmp;
534  //mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes );
535  QgsFeatureRequest request;
537  if ( subsetAttrs )
538  {
540  }
542  if ( fi.nextFeature( tmp ) )
543  {
546  f.setAttributes( tmp.attributes() );
547  }
548  }
549 
551 }
552 
553 
554 
556 {
558 
561 }
562 
564 {
565  if ( !mSource->mFields.exists( fieldIdx ) )
566  return;
567 
568  if ( mSource->mFields.fieldOrigin( fieldIdx ) != QgsFields::OriginJoin )
569  return;
570 
571  int sourceLayerIndex;
572  const QgsVectorLayerJoinInfo *joinInfo = mSource->mJoinBuffer->joinForFieldIndex( fieldIdx, mSource->mFields, sourceLayerIndex );
573  Q_ASSERT( joinInfo );
574 
575  QgsVectorLayer *joinLayer = joinInfo->joinLayer();
576  if ( !joinLayer )
577  return; // invalid join (unresolved reference to layer)
578 
579  if ( !mFetchJoinInfo.contains( joinInfo ) )
580  {
581  FetchJoinInfo info;
582  info.joinInfo = joinInfo;
583  info.joinLayer = joinLayer;
586  info.joinField = joinLayer->fields().indexFromName( joinInfo->joinFieldName() );
587 
588  // for joined fields, we always need to request the targetField from the provider too
589  if ( !mPreparedFields.contains( info.targetField ) && !mFieldsToPrepare.contains( info.targetField ) )
590  mFieldsToPrepare << info.targetField;
591 
594 
595  mFetchJoinInfo.insert( joinInfo, info );
596  }
597 
598  // store field source index - we'll need it when fetching from provider
599  mFetchJoinInfo[ joinInfo ].attributes.push_back( sourceLayerIndex );
600 }
601 
603 {
604  const QList<QgsExpressionFieldBuffer::ExpressionField> &exps = mSource->mExpressionFieldBuffer->expressions();
605 
606  int oi = mSource->mFields.fieldOriginIndex( fieldIdx );
607  QgsExpression *exp = new QgsExpression( exps[oi].cachedExpression );
608 
609  QgsDistanceArea da;
610  da.setSourceCrs( mSource->mCrs );
611  da.setEllipsoid( QgsProject::instance()->ellipsoid() );
612  exp->setGeomCalculator( &da );
613  exp->setDistanceUnits( QgsProject::instance()->distanceUnits() );
614  exp->setAreaUnits( QgsProject::instance()->areaUnits() );
615 
616  exp->prepare( mExpressionContext.get() );
617  mExpressionFieldInfo.insert( fieldIdx, exp );
618 
619  Q_FOREACH ( const QString &col, exp->referencedColumns() )
620  {
621  int dependentFieldIdx = mSource->mFields.lookupField( col );
623  {
624  mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() << dependentFieldIdx );
625  }
626  // also need to fetch this dependent field
627  if ( !mPreparedFields.contains( dependentFieldIdx ) && !mFieldsToPrepare.contains( dependentFieldIdx ) )
628  mFieldsToPrepare << dependentFieldIdx;
629  }
630 
631  if ( exp->needsGeometry() )
632  {
633  mRequest.setFlags( mRequest.flags() & ~QgsFeatureRequest::NoGeometry );
634  }
635 }
636 
638 {
639  mPreparedFields.clear();
640  mFieldsToPrepare.clear();
641  mFetchJoinInfo.clear();
642  mOrderedJoinInfoList.clear();
643 
644  mExpressionContext.reset( new QgsExpressionContext() );
645  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
646  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );
647  mExpressionContext->setFields( mSource->mFields );
648 
650 
651  while ( !mFieldsToPrepare.isEmpty() )
652  {
653  int fieldIdx = mFieldsToPrepare.takeFirst();
654  if ( mPreparedFields.contains( fieldIdx ) )
655  continue;
656 
657  mPreparedFields << fieldIdx;
658  prepareField( fieldIdx );
659  }
660 
661  //sort joins by dependency
662  if ( mFetchJoinInfo.size() > 0 )
663  {
664  createOrderedJoinList();
665  }
666 }
667 
668 void QgsVectorLayerFeatureIterator::createOrderedJoinList()
669 {
670  mOrderedJoinInfoList = mFetchJoinInfo.values();
671  if ( mOrderedJoinInfoList.size() < 2 )
672  {
673  return;
674  }
675 
676  QSet<int> resolvedFields; //todo: get provider / virtual fields without joins
677 
678  //add all provider fields without joins as resolved fields
679  QList< int >::const_iterator prepFieldIt = mPreparedFields.constBegin();
680  for ( ; prepFieldIt != mPreparedFields.constEnd(); ++prepFieldIt )
681  {
682  if ( mSource->mFields.fieldOrigin( *prepFieldIt ) != QgsFields::OriginJoin )
683  {
684  resolvedFields.insert( *prepFieldIt );
685  }
686  }
687 
688  //iterate through the joins. If target field is not yet covered, move the entry to the end of the list
689 
690  //some join combinations might not have a resolution at all
691  int maxIterations = ( mOrderedJoinInfoList.size() + 1 ) * mOrderedJoinInfoList.size() / 2.0;
692  int currentIteration = 0;
693 
694  for ( int i = 0; i < mOrderedJoinInfoList.size() - 1; ++i )
695  {
696  if ( !resolvedFields.contains( mOrderedJoinInfoList.at( i ).targetField ) )
697  {
698  mOrderedJoinInfoList.append( mOrderedJoinInfoList.at( i ) );
699  mOrderedJoinInfoList.removeAt( i );
700  --i;
701  }
702  else
703  {
704  int offset = mOrderedJoinInfoList.at( i ).indexOffset;
705  int joinField = mOrderedJoinInfoList.at( i ).joinField;
706 
707  QgsAttributeList attributes = mOrderedJoinInfoList.at( i ).attributes;
708  QgsAttributeList::const_iterator attIt = attributes.constBegin();
709  for ( ; attIt != attributes.constEnd(); ++attIt )
710  {
711  if ( *attIt != joinField )
712  {
713  resolvedFields.insert( joinField < *attIt ? *attIt + offset - 1 : *attIt + offset );
714  }
715  }
716  }
717 
718  ++currentIteration;
719  if ( currentIteration >= maxIterations )
720  {
721  break;
722  }
723  }
724 }
725 
726 bool QgsVectorLayerFeatureIterator::postProcessFeature( QgsFeature &feature )
727 {
728  bool result = checkGeometryValidity( feature );
729  if ( result )
731  return result;
732 }
733 
734 bool QgsVectorLayerFeatureIterator::checkGeometryValidity( const QgsFeature &feature )
735 {
736  if ( !feature.hasGeometry() )
737  return true;
738 
739  switch ( mRequest.invalidGeometryCheck() )
740  {
742  return true;
743 
745  {
746  if ( !feature.geometry().isGeosValid() )
747  {
748  QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), QgsMessageLog::CRITICAL );
750  {
751  mRequest.invalidGeometryCallback()( feature );
752  }
753  return false;
754  }
755  break;
756  }
757 
759  if ( !feature.geometry().isGeosValid() )
760  {
761  QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), QgsMessageLog::CRITICAL );
762  close();
764  {
765  mRequest.invalidGeometryCallback()( feature );
766  }
767  return false;
768  }
769  break;
770  }
771 
772  return true;
773 }
774 
776 {
777  switch ( mSource->mFields.fieldOrigin( fieldIdx ) )
778  {
780  prepareExpression( fieldIdx );
781  break;
782 
785  {
786  prepareJoin( fieldIdx );
787  }
788  break;
789 
793  break;
794  }
795 }
796 
798 {
799  QList< FetchJoinInfo >::const_iterator joinIt = mOrderedJoinInfoList.constBegin();
800  for ( ; joinIt != mOrderedJoinInfoList.constEnd(); ++joinIt )
801  {
802  QVariant targetFieldValue = f.attribute( joinIt->targetField );
803  if ( !targetFieldValue.isValid() )
804  continue;
805 
806  const QHash< QString, QgsAttributes> &memoryCache = joinIt->joinInfo->cachedAttributes;
807  if ( memoryCache.isEmpty() )
808  joinIt->addJoinedAttributesDirect( f, targetFieldValue );
809  else
810  joinIt->addJoinedAttributesCached( f, targetFieldValue );
811  }
812 }
813 
815 {
816  // make sure we have space for newly added attributes
817  QgsAttributes attr = f.attributes();
818  attr.resize( mSource->mFields.count() ); // Provider attrs count + joined attrs count + expression attrs count
819  f.setAttributes( attr );
820 
821  // possible TODO - handle combinations of expression -> join -> expression -> join?
822  // but for now, write that off as too complex and an unlikely rare, unsupported use case
823 
824  QList< int > fetchedVirtualAttributes;
825  //first, check through joins for any virtual fields we need
826  QMap<const QgsVectorLayerJoinInfo *, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
827  for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
828  {
829  if ( mExpressionFieldInfo.contains( joinIt->targetField ) )
830  {
831  // have to calculate expression field before we can handle this join
832  addExpressionAttribute( f, joinIt->targetField );
833  fetchedVirtualAttributes << joinIt->targetField;
834  }
835  }
836 
837  if ( !mFetchJoinInfo.isEmpty() )
838  addJoinedAttributes( f );
839 
840  // add remaining expression fields
841  if ( !mExpressionFieldInfo.isEmpty() )
842  {
843  QMap<int, QgsExpression *>::ConstIterator it = mExpressionFieldInfo.constBegin();
844  for ( ; it != mExpressionFieldInfo.constEnd(); ++it )
845  {
846  if ( fetchedVirtualAttributes.contains( it.key() ) )
847  continue;
848 
849  addExpressionAttribute( f, it.key() );
850  }
851  }
852 }
853 
855 {
856  QgsExpression *exp = mExpressionFieldInfo.value( attrIndex );
857  if ( exp )
858  {
859  mExpressionContext->setFeature( f );
860  QVariant val = exp->evaluate( mExpressionContext.get() );
861  mSource->mFields.at( attrIndex ).convertCompatible( val );
862  f.setAttribute( attrIndex, val );
863  }
864  else
865  {
866  f.setAttribute( attrIndex, QVariant() );
867  }
868 }
869 
871 {
872  Q_UNUSED( simplifyMethod );
873  return false;
874 }
875 
876 bool QgsVectorLayerFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
877 {
878  Q_UNUSED( methodType );
879  return false;
880 }
881 
882 
884 {
885  const QHash<QString, QgsAttributes> &memoryCache = joinInfo->cachedAttributes;
886  QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
887  if ( it == memoryCache.constEnd() )
888  return; // joined value not found -> leaving the attributes empty (null)
889 
890  int index = indexOffset;
891 
892  const QgsAttributes &featureAttributes = it.value();
893  for ( int i = 0; i < featureAttributes.count(); ++i )
894  {
895  f.setAttribute( index++, featureAttributes.at( i ) );
896  }
897 }
898 
899 
900 
902 {
903  // no memory cache, query the joined values by setting substring
904  QString subsetString;
905 
906  QString joinFieldName = joinInfo->joinFieldName();
907 
908  subsetString.append( QStringLiteral( "\"%1\"" ).arg( joinFieldName ) );
909 
910  if ( joinValue.isNull() )
911  {
912  subsetString += QLatin1String( " IS NULL" );
913  }
914  else
915  {
916  QString v = joinValue.toString();
917  switch ( joinValue.type() )
918  {
919  case QVariant::Int:
920  case QVariant::LongLong:
921  case QVariant::Double:
922  break;
923 
924  default:
925  case QVariant::String:
926  v.replace( '\'', QLatin1String( "''" ) );
927  v.prepend( '\'' ).append( '\'' );
928  break;
929  }
930  subsetString += '=' + v;
931  }
932 
933  // maybe user requested just a subset of layer's attributes
934  // so we do not have to cache everything
935  bool hasSubset = joinInfo->joinFieldNamesSubset();
936  QVector<int> subsetIndices;
937  if ( hasSubset )
938  subsetIndices = QgsVectorLayerJoinBuffer::joinSubsetIndices( joinLayer, *joinInfo->joinFieldNamesSubset() );
939 
940  // select (no geometry)
941  QgsFeatureRequest request;
943  request.setSubsetOfAttributes( attributes );
944  request.setFilterExpression( subsetString );
945  request.setLimit( 1 );
946  QgsFeatureIterator fi = joinLayer->getFeatures( request );
947 
948  // get first feature
949  QgsFeature fet;
950  if ( fi.nextFeature( fet ) )
951  {
952  int index = indexOffset;
953  QgsAttributes attr = fet.attributes();
954  if ( hasSubset )
955  {
956  for ( int i = 0; i < subsetIndices.count(); ++i )
957  f.setAttribute( index++, attr.at( subsetIndices.at( i ) ) );
958  }
959  else
960  {
961  // use all fields except for the one used for join (has same value as exiting field in target layer)
962  for ( int i = 0; i < attr.count(); ++i )
963  {
964  if ( i == joinField )
965  continue;
966 
967  f.setAttribute( index++, attr.at( i ) );
968  }
969  }
970  }
971  else
972  {
973  // no suitable join feature found, keeping empty (null) attributes
974  }
975 }
976 
977 
978 
979 
981 {
982  QgsFeatureId featureId = mRequest.filterFid();
983 
984  // deleted already?
985  if ( mSource->mDeletedFeatureIds.contains( featureId ) )
986  return false;
987 
988  // has changed geometry?
989  if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) )
990  {
991  useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f );
992  return true;
993  }
994 
995  // added features
996  for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter )
997  {
998  if ( iter->id() == featureId )
999  {
1000  useAddedFeature( *iter, f );
1001  return true;
1002  }
1003  }
1004 
1005  // regular features
1007  if ( fi.nextFeature( f ) )
1008  {
1009  f.setFields( mSource->mFields );
1010 
1011  if ( mSource->mHasEditBuffer )
1013 
1014  if ( mHasVirtualAttributes )
1015  addVirtualAttributes( f );
1016 
1017  return true;
1018  }
1019 
1020  return false;
1021 }
1022 
1024 {
1025  QgsAttributes attrs = f.attributes();
1026 
1027  // remove all attributes that will disappear - from higher indices to lower
1028  for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
1029  {
1030  attrs.remove( mSource->mDeletedAttributeIds[idx] );
1031  }
1032 
1033  // adjust size to accommodate added attributes
1034  attrs.resize( attrs.count() + mSource->mAddedAttributes.count() );
1035 
1036  // update changed attributes
1037  if ( mSource->mChangedAttributeValues.contains( f.id() ) )
1038  {
1040  for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
1041  attrs[it.key()] = it.value();
1042  }
1043  f.setAttributes( attrs );
1044 }
1045 
1047 {
1048  if ( mSource->mChangedGeometries.contains( f.id() ) )
1050 }
1051 
1052 bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys )
1053 {
1054  Q_UNUSED( orderBys );
1055  return true;
1056 }
1057 
1058 
1059 //
1060 // QgsVectorLayerSelectedFeatureSource
1061 //
1062 
1064  : mSource( layer )
1065  , mSelectedFeatureIds( layer->selectedFeatureIds() )
1066  , mWkbType( layer->wkbType() )
1067  , mName( layer->name() )
1068 {}
1069 
1071 {
1072  QgsFeatureRequest req( request );
1073 
1074  if ( req.filterFids().isEmpty() && req.filterType() != QgsFeatureRequest::FilterFid )
1075  {
1076  req.setFilterFids( mSelectedFeatureIds );
1077  }
1078  else if ( !req.filterFids().isEmpty() )
1079  {
1080  QgsFeatureIds reqIds = mSelectedFeatureIds;
1081  reqIds.intersect( req.filterFids() );
1082  req.setFilterFids( reqIds );
1083  }
1084 
1085  return mSource.getFeatures( req );
1086 }
1087 
1089 {
1090  return mSource.crs();
1091 }
1092 
1094 {
1095  return mSource.fields();
1096 }
1097 
1099 {
1100  return mWkbType;
1101 }
1102 
1104 {
1105  return mSelectedFeatureIds.count();
1106 }
1107 
1109 {
1110  return mName;
1111 }
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:289
QList< QgsExpressionFieldBuffer::ExpressionField > expressions() const
QgsAbstractFeatureSource * mProviderFeatureSource
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Returns an iterator for the features in the source.
QgsFeatureId id
Definition: qgsfeature.h:70
void addJoinedAttributesDirect(QgsFeature &f, const QVariant &joinValue) const
Wrapper for iterator of features from vector data provider or vector layer.
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
Definition: qgsfeature.h:512
const QgsVectorLayerJoinInfo * joinInfo
Canonical source of information about the join.
QString targetFieldName() const
Returns name of the field of our layer that will be used for join.
long limit() const
Returns the maximum number of features to request, or -1 if no limit set.
bool acceptFeature(const QgsFeature &feature)
Check if a feature is accepted by this requests filter.
QSet< QString > CORE_EXPORT usedAttributes() const
Returns a set of used attributes.
void geometryToDestinationCrs(QgsFeature &feature, const QgsCoordinateTransform &transform) const
Transforms feature&#39;s geometry according to the specified coordinate transform.
QgsCoordinateReferenceSystem sourceCrs() const override
Returns the coordinate reference system for features in the source.
Filter using feature ID.
QgsVectorLayerJoinBuffer * mJoinBuffer
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system for features retrieved from this source.
bool containsJoins() const
Quick way to test if there is any join at all.
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:49
virtual void setInterruptionChecker(QgsInterruptionChecker *interruptionChecker) override
Attach an object that can be queried regularly by the iterator to check if it must stopped...
FieldOrigin fieldOrigin(int fieldIdx) const
Get field&#39;s origin (value from an enumeration)
Definition: qgsfields.cpp:161
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:155
QString sourceName() const override
Returns a friendly display name for the source.
const Flags & flags() const
void createJoinCaches()
Calls cacheJoinLayer() for all vector joins.
QMap< int, QgsExpression * > mExpressionFieldInfo
void addExpressionAttribute(QgsFeature &f, int attrIndex)
Adds an expression based attribute to a feature.
QgsFeatureMap::ConstIterator mFetchAddedFeaturesIt
QgsGeometryMap::ConstIterator mFetchChangedGeomIt
Field has been temporarily added in editing mode (originIndex = index in the list of added attributes...
Definition: qgsfields.h:50
bool exists(int i) const
Return if a field index is valid.
Definition: qgsfields.cpp:125
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:519
QgsVectorLayerFeatureIterator(QgsVectorLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request)
QgsVectorLayerSelectedFeatureSource(QgsVectorLayer *layer)
Constructor for QgsVectorLayerSelectedFeatureSource, for selected features from the specified layer...
QgsExpressionContext * expressionContext()
Returns the expression context used to evaluate filter expressions.
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) override
Get an iterator for features matching the specified request.
QgsFeatureId filterFid() const
Get the feature ID that should be fetched.
QgsRectangle filterRectToSourceCrs(const QgsCoordinateTransform &transform) const
Returns a rectangle representing the original request&#39;s QgsFeatureRequest::filterRect().
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for feature&#39;s geometries, or an invalid QgsCoordi...
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())=0
Get an iterator for features matching the specified request.
bool convertCompatible(QVariant &v) const
Converts the provided variant to a compatible format.
Definition: qgsfield.cpp:225
FilterType filterType() const
Return the filter type which is currently set on this request.
Container of fields for a vector layer.
Definition: qgsfields.h:41
const QgsFeatureIds & filterFids() const
Get feature IDs that should be fetched.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:96
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:127
bool setAttribute(int field, const QVariant &attr)
Set an attribute&#39;s value by field index.
Definition: qgsfeature.cpp:204
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
Field comes from the underlying data provider of the vector layer (originIndex = index in provider&#39;s ...
Definition: qgsfields.h:48
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
bool mClosed
Set to true, as soon as the iterator is closed.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:61
QgsChangedAttributesMap changedAttributeValues() const
Returns a map of features with changed attributes values which are not committed. ...
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:190
QgsVectorLayer * joinLayer
Resolved pointer to the joined layer.
const QgsRectangle & filterRect() const
Returns the rectangle from which features will be taken.
int count() const
Return number of items.
Definition: qgsfields.cpp:115
QgsFeatureIds deletedFeatureIds() const
Returns a list of deleted feature IDs which are not committed.
bool isGeosValid() const
Checks validity of the geometry using GEOS.
It has not been specified where the field comes from.
Definition: qgsfields.h:47
QgsFields fields() const override
Returns the fields associated with features in the source.
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:135
int joinField
Index of field (of the joined layer) must have equal value.
QgsExpression * filterExpression() const
Returns the filter expression if set.
int fieldOriginIndex(int fieldIdx) const
Get field&#39;s origin index (its meaning is specific to each type of origin)
Definition: qgsfields.cpp:169
virtual QgsAbstractFeatureSource * featureSource() const =0
Return feature source object that can be used for querying provider&#39;s data.
Interface that can be optionally attached to an iterator so its nextFeature() implementaton can check...
void updateChangedAttributes(QgsFeature &f)
Update feature with uncommitted attribute updates.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Set feature ID that should be fetched.
QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on...
QgsGeometryMap changedGeometries() const
Returns a map of features with changed geometries which are not committed.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:66
void iteratorClosed()
to be called by from subclass in close()
int indexFromName(const QString &fieldName) const
Get the field index from the field name.
Definition: qgsfields.cpp:174
void useAddedFeature(const QgsFeature &src, QgsFeature &f)
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QgsVectorLayer * joinLayer() const
Returns joined layer (may be null if the reference was set by layer ID and not resolved yet) ...
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
Definition: qgsfields.cpp:316
QgsFields fields() const override
Returns the list of fields of this layer.
QgsFields fields() const
Returns the fields that will be available for features that are retrieved from this source...
QgsFeatureRequest & disableFilter()
Disables filter conditions.
void updateFeatureGeometry(QgsFeature &f)
Update feature with uncommitted geometry updates.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
virtual bool prepareSimplification(const QgsSimplifyMethod &simplifyMethod) override
Setup the simplification of geometries to fetch using the specified simplify method.
void addJoinedAttributesCached(QgsFeature &f, const QVariant &joinValue) const
Defines left outer join from our vector layer to some other vector layer.
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:39
virtual bool fetchFeature(QgsFeature &feature) override
fetch next feature, return true on success
QgsAttributeList deletedAttributeIds() const
Returns a list of deleted attributes fields which are not committed.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
QgsWkbTypes::Type wkbType() const override
Returns the geometry type for features returned by this source.
No invalid geometry checking.
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag)
void setId(QgsFeatureId id)
Sets the feature ID for this feature.
Definition: qgsfeature.cpp:112
QgsExpressionFieldBuffer * mExpressionFieldBuffer
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
QgsAttributeList subsetOfAttributes() const
Return the subset of attributes which at least need to be fetched.
Partial snapshot of vector layer&#39;s state (only the members necessary for access to features) ...
int indexOffset
At what position the joined fields start.
void useChangedAttributeFeature(QgsFeatureId fid, const QgsGeometry &geom, QgsFeature &f)
A general purpose distance and area calculator, capable of performing ellipsoid based calculations...
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:181
QMap< QgsFeatureId, QgsFeature > QgsFeatureMap
long featureCount() const override
Returns the number of features contained in the source, or -1 if the feature count is unknown...
QgsFeatureMap addedFeatures() const
Returns a map of new features which are not committed.
QgsFeatureRequest mRequest
A copy of the feature request.
static void logMessage(const QString &message, const QString &tag=QString(), MessageLevel level=QgsMessageLog::WARNING)
add a message to the instance (and create it if necessary)
int targetField
Index of field (of this layer) that drives the join.
Buffers information about expression fields for a vector layer.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Set feature IDs that should be fetched.
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
Definition: qgsfeature.h:503
virtual bool rewind() override
reset the iterator to the starting position
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:377
QMap< const QgsVectorLayerJoinInfo *, QgsVectorLayerFeatureIterator::FetchJoinInfo > mFetchJoinInfo
Information about joins used in the current select() statement.
This class represents a coordinate reference system (CRS).
virtual bool close() override
end of iterating: free the resources / lock
static QVector< int > joinSubsetIndices(QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset)
Return a vector of indices for use in join based on field names from the layer.
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
InvalidGeometryCheck invalidGeometryCheck() const
Returns the invalid geometry checking behavior.
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
QgsVectorDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
Class for doing transforms between two map coordinate systems.
Join information prepared for fast attribute id mapping in QgsVectorLayerJoinBuffer::updateFeatureAtt...
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs for feature&#39;s geometries.
qint64 QgsFeatureId
Definition: qgsfeature.h:37
This class contains information about how to simplify geometries fetched from a QgsFeatureIterator.
QgsCoordinateReferenceSystem mCrs
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:62
void setSourceCrs(const QgsCoordinateReferenceSystem &srcCRS)
Sets source spatial reference system.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
bool nextFeature(QgsFeature &f)
QgsChangedAttributesMap mChangedAttributeValues
OrderBy orderBy() const
Return a list of order by clauses specified for this feature request.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QList< QgsField > addedAttributes() const
Returns a list of added attributes fields which are not committed.
A vector of attributes.
Definition: qgsattributes.h:57
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:255
Field is calculated from an expression.
Definition: qgsfields.h:51
QString joinFieldName() const
Returns name of the field of joined layer that will be used for join.
bool isClosed() const
find out whether the iterator is still valid or closed already
QgsVectorLayerFeatureSource(const QgsVectorLayer *layer)
Constructor for QgsVectorLayerFeatureSource.
QgsAttributes attributes
Definition: qgsfeature.h:71
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
std::function< void(const QgsFeature &) > invalidGeometryCallback() const
Returns the callback function to use when encountering an invalid geometry and invalidGeometryCheck()...
void setInterruptionChecker(QgsInterruptionChecker *interruptionChecker)
Attach an object that can be queried regularly by the iterator to check if it must stopped...
int joinedFieldsOffset(const QgsVectorLayerJoinInfo *info, const QgsFields &fields)
Find out what is the first index of the join within fields.
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer.
void addVirtualAttributes(QgsFeature &f)
Adds attributes that don&#39;t source from the provider but are added inside QGIS Includes.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Set flags that affect how features will be fetched.
Helper template that cares of two things: 1.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.