@@ -95,11 +95,7 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeat
95
95
, mFetchedFid( false )
96
96
, mEditGeometrySimplifier( nullptr )
97
97
{
98
- prepareExpressions ();
99
-
100
- // prepare joins: may add more attributes to fetch (in order to allow join)
101
- if ( mSource ->mJoinBuffer ->containsJoins () )
102
- prepareJoins ();
98
+ prepareFields ();
103
99
104
100
mHasVirtualAttributes = !mFetchJoinInfo .isEmpty () || !mExpressionFieldInfo .isEmpty ();
105
101
@@ -472,128 +468,132 @@ void QgsVectorLayerFeatureIterator::rewindEditBuffer()
472
468
mFetchChangedGeomIt = mSource ->mChangedGeometries .constBegin ();
473
469
}
474
470
471
+ void QgsVectorLayerFeatureIterator::prepareJoin ( int fieldIdx )
472
+ {
473
+ if ( !mSource ->mFields .exists ( fieldIdx ) )
474
+ return ;
475
475
476
+ if ( mSource ->mFields .fieldOrigin ( fieldIdx ) != QgsFields::OriginJoin )
477
+ return ;
476
478
477
- void QgsVectorLayerFeatureIterator::prepareJoins ()
478
- {
479
- QgsAttributeList fetchAttributes = ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest .subsetOfAttributes () : mSource ->mFields .allAttributesList ();
480
- QgsAttributeList sourceJoinFields; // attributes that also need to be fetched from this layer in order to have joins working
479
+ int sourceLayerIndex;
480
+ const QgsVectorJoinInfo* joinInfo = mSource ->mJoinBuffer ->joinForFieldIndex ( fieldIdx, mSource ->mFields , sourceLayerIndex );
481
+ Q_ASSERT ( joinInfo );
481
482
482
- mFetchJoinInfo .clear ();
483
+ QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance ()->mapLayer ( joinInfo->joinLayerId ) );
484
+ Q_ASSERT ( joinLayer );
483
485
484
- for ( QgsAttributeList::const_iterator attIt = fetchAttributes. constBegin (); attIt != fetchAttributes. constEnd (); ++attIt )
486
+ if ( ! mFetchJoinInfo . contains ( joinInfo ) )
485
487
{
486
- if ( !mSource ->mFields .exists ( *attIt ) )
487
- continue ;
488
+ FetchJoinInfo info;
489
+ info.joinInfo = joinInfo;
490
+ info.joinLayer = joinLayer;
491
+ info.indexOffset = mSource ->mJoinBuffer ->joinedFieldsOffset ( joinInfo, mSource ->mFields );
488
492
489
- if ( mSource ->mFields .fieldOrigin ( *attIt ) != QgsFields::OriginJoin )
490
- continue ;
493
+ if ( joinInfo->targetFieldName .isEmpty () )
494
+ info.targetField = joinInfo->targetFieldIndex ; // for compatibility with 1.x
495
+ else
496
+ info.targetField = mSource ->mFields .indexFromName ( joinInfo->targetFieldName );
491
497
492
- int sourceLayerIndex;
493
- const QgsVectorJoinInfo* joinInfo = mSource ->mJoinBuffer ->joinForFieldIndex ( *attIt, mSource ->mFields , sourceLayerIndex );
494
- Q_ASSERT ( joinInfo );
498
+ if ( joinInfo->joinFieldName .isEmpty () )
499
+ info.joinField = joinInfo->joinFieldIndex ; // for compatibility with 1.x
500
+ else
501
+ info.joinField = joinLayer->fields ().indexFromName ( joinInfo->joinFieldName );
495
502
496
- QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance ()->mapLayer ( joinInfo->joinLayerId ) );
497
- Q_ASSERT ( joinLayer );
503
+ // for joined fields, we always need to request the targetField from the provider too
504
+ if ( !mPreparedFields .contains ( info.targetField ) && !mFieldsToPrepare .contains ( info.targetField ) )
505
+ mFieldsToPrepare << info.targetField ;
498
506
499
- if ( !mFetchJoinInfo .contains ( joinInfo ) )
500
- {
501
- FetchJoinInfo info;
502
- info.joinInfo = joinInfo;
503
- info.joinLayer = joinLayer;
504
- info.indexOffset = mSource ->mJoinBuffer ->joinedFieldsOffset ( joinInfo, mSource ->mFields );
505
-
506
- if ( joinInfo->targetFieldName .isEmpty () )
507
- info.targetField = joinInfo->targetFieldIndex ; // for compatibility with 1.x
508
- else
509
- info.targetField = mSource ->mFields .indexFromName ( joinInfo->targetFieldName );
510
-
511
- if ( joinInfo->joinFieldName .isEmpty () )
512
- info.joinField = joinInfo->joinFieldIndex ; // for compatibility with 1.x
513
- else
514
- info.joinField = joinLayer->fields ().indexFromName ( joinInfo->joinFieldName );
515
-
516
- // for joined fields, we always need to request the targetField from the provider too
517
- if ( !fetchAttributes.contains ( info.targetField ) )
518
- sourceJoinFields << info.targetField ;
519
-
520
- mFetchJoinInfo .insert ( joinInfo, info );
521
- }
507
+ if ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes && !mRequest .subsetOfAttributes ().contains ( info.targetField ) )
508
+ mRequest .setSubsetOfAttributes ( mRequest .subsetOfAttributes () << info.targetField );
522
509
523
- // store field source index - we'll need it when fetching from provider
524
- mFetchJoinInfo [ joinInfo ].attributes .push_back ( sourceLayerIndex );
510
+ mFetchJoinInfo .insert ( joinInfo, info );
525
511
}
526
512
527
- // add sourceJoinFields if we're using a subset
528
- if ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes )
529
- mRequest .setSubsetOfAttributes ( mRequest .subsetOfAttributes () + sourceJoinFields );
513
+ // store field source index - we'll need it when fetching from provider
514
+ mFetchJoinInfo [ joinInfo ].attributes .push_back ( sourceLayerIndex );
530
515
}
531
516
532
- void QgsVectorLayerFeatureIterator::prepareExpressions ( )
517
+ void QgsVectorLayerFeatureIterator::prepareExpression ( int fieldIdx )
533
518
{
534
- const QList<QgsExpressionFieldBuffer::ExpressionField> exps = mSource ->mExpressionFieldBuffer ->expressions ();
519
+ const QList<QgsExpressionFieldBuffer::ExpressionField>& exps = mSource ->mExpressionFieldBuffer ->expressions ();
535
520
536
- mExpressionContext .reset ( new QgsExpressionContext () );
537
- mExpressionContext ->appendScope ( QgsExpressionContextUtils::globalScope () );
538
- mExpressionContext ->appendScope ( QgsExpressionContextUtils::projectScope () );
539
- mExpressionContext ->setFields ( mSource ->mFields );
521
+ int oi = mSource ->mFields .fieldOriginIndex ( fieldIdx );
522
+ QgsExpression* exp = new QgsExpression ( exps[oi].cachedExpression );
540
523
541
- QList< int > virtualFieldsToFetch;
542
- for ( int i = 0 ; i < mSource ->mFields .count (); i++ )
524
+ QgsDistanceArea da;
525
+ da.setSourceCrs ( mSource ->mCrsId );
526
+ da.setEllipsoidalMode ( true );
527
+ da.setEllipsoid ( QgsProject::instance ()->readEntry ( " Measure" , " /Ellipsoid" , GEO_NONE ) );
528
+ exp ->setGeomCalculator ( da );
529
+ exp ->setDistanceUnits ( QgsProject::instance ()->distanceUnits () );
530
+ exp ->setAreaUnits ( QgsProject::instance ()->areaUnits () );
531
+
532
+ exp ->prepare ( mExpressionContext .data () );
533
+ mExpressionFieldInfo .insert ( fieldIdx, exp );
534
+
535
+ Q_FOREACH ( const QString& col, exp ->referencedColumns () )
543
536
{
544
- if ( mSource ->mFields .fieldOrigin ( i ) == QgsFields::OriginExpression )
537
+ int dependantFieldIdx = mSource ->mFields .fieldNameIndex ( col );
538
+ if ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes )
545
539
{
546
- // Only prepare if there is no subset defined or the subset contains this field
547
- if ( !( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes )
548
- || mRequest .subsetOfAttributes ().contains ( i ) )
549
- {
550
- virtualFieldsToFetch << i;
551
- }
540
+ mRequest .setSubsetOfAttributes ( mRequest .subsetOfAttributes () << dependantFieldIdx );
552
541
}
542
+ // also need to fetch this dependant field
543
+ if ( !mPreparedFields .contains ( dependantFieldIdx ) && !mFieldsToPrepare .contains ( dependantFieldIdx ) )
544
+ mFieldsToPrepare << dependantFieldIdx;
553
545
}
554
546
555
- QList< int > virtualFieldsProcessed;
556
- while ( !virtualFieldsToFetch.isEmpty () )
547
+ if ( exp ->needsGeometry () )
557
548
{
558
- int fieldIdx = virtualFieldsToFetch. takeFirst ( );
559
- if ( virtualFieldsProcessed. contains ( fieldIdx ) )
560
- continue ;
549
+ mRequest . setFlags ( mRequest . flags () & ~QgsFeatureRequest::NoGeometry );
550
+ }
551
+ }
561
552
562
- virtualFieldsProcessed << fieldIdx;
553
+ void QgsVectorLayerFeatureIterator::prepareFields ()
554
+ {
555
+ mPreparedFields .clear ();
556
+ mFieldsToPrepare .clear ();
557
+ mFetchJoinInfo .clear ();
563
558
559
+ mExpressionContext .reset ( new QgsExpressionContext () );
560
+ mExpressionContext ->appendScope ( QgsExpressionContextUtils::globalScope () );
561
+ mExpressionContext ->appendScope ( QgsExpressionContextUtils::projectScope () );
562
+ mExpressionContext ->setFields ( mSource ->mFields );
564
563
565
- int oi = mSource ->mFields .fieldOriginIndex ( fieldIdx );
566
- QgsExpression* exp = new QgsExpression ( exps[oi].cachedExpression );
564
+ mFieldsToPrepare = ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest .subsetOfAttributes () : mSource ->mFields .allAttributesList ();
567
565
568
- QgsDistanceArea da;
569
- da.setSourceCrs ( mSource ->mCrsId );
570
- da.setEllipsoidalMode ( true );
571
- da.setEllipsoid ( QgsProject::instance ()->readEntry ( " Measure" , " /Ellipsoid" , GEO_NONE ) );
572
- exp ->setGeomCalculator ( da );
573
- exp ->setDistanceUnits ( QgsProject::instance ()->distanceUnits () );
574
- exp ->setAreaUnits ( QgsProject::instance ()->areaUnits () );
566
+ while ( !mFieldsToPrepare .isEmpty () )
567
+ {
568
+ int fieldIdx = mFieldsToPrepare .takeFirst ();
569
+ if ( mPreparedFields .contains ( fieldIdx ) )
570
+ continue ;
575
571
576
- exp ->prepare ( mExpressionContext .data () );
577
- mExpressionFieldInfo .insert ( fieldIdx, exp );
572
+ mPreparedFields << fieldIdx;
573
+ prepareField ( fieldIdx );
574
+ }
575
+ }
578
576
579
- Q_FOREACH ( const QString& col, exp ->referencedColumns () )
580
- {
581
- int dependantFieldIdx = mSource ->mFields .fieldNameIndex ( col );
582
- if ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes )
577
+ void QgsVectorLayerFeatureIterator::prepareField ( int fieldIdx )
578
+ {
579
+ switch ( mSource ->mFields .fieldOrigin ( fieldIdx ) )
580
+ {
581
+ case QgsFields::OriginExpression:
582
+ prepareExpression ( fieldIdx );
583
+ break ;
584
+
585
+ case QgsFields::OriginJoin:
586
+ if ( mSource ->mJoinBuffer ->containsJoins () )
583
587
{
584
- mRequest . setSubsetOfAttributes ( mRequest . subsetOfAttributes () << dependantFieldIdx );
588
+ prepareJoin ( fieldIdx );
585
589
}
586
- // also need to fetch this dependant field
587
- if ( mSource ->mFields .fieldOrigin ( dependantFieldIdx ) == QgsFields::OriginExpression )
588
- virtualFieldsToFetch << dependantFieldIdx;
589
- }
590
+ break ;
590
591
591
- if ( exp -> needsGeometry () )
592
- {
593
- mRequest . setFlags ( mRequest . flags () & ~QgsFeatureRequest::NoGeometry );
594
- }
592
+ case QgsFields::OriginUnknown:
593
+ case QgsFields::OriginProvider:
594
+ case QgsFields::OriginEdit:
595
+ break ;
595
596
}
596
-
597
597
}
598
598
599
599
void QgsVectorLayerFeatureIterator::addJoinedAttributes ( QgsFeature &f )
@@ -623,24 +623,48 @@ void QgsVectorLayerFeatureIterator::addVirtualAttributes( QgsFeature& f )
623
623
attr.resize ( mSource ->mFields .count () ); // Provider attrs count + joined attrs count + expression attrs count
624
624
f.setAttributes ( attr );
625
625
626
+ // possible TODO - handle combinations of expression -> join -> expression -> join?
627
+ // but for now, write that off as too complex and an unlikely rare, unsupported use case
628
+
629
+ QList< int > fetchedVirtualAttributes;
630
+ // first, check through joins for any virtual fields we need
631
+ QMap<const QgsVectorJoinInfo*, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo .constBegin ();
632
+ for ( ; joinIt != mFetchJoinInfo .constEnd (); ++joinIt )
633
+ {
634
+ if ( mExpressionFieldInfo .contains ( joinIt->targetField ) )
635
+ {
636
+ // have to calculate expression field before we can handle this join
637
+ addExpressionAttribute ( f, joinIt->targetField );
638
+ fetchedVirtualAttributes << joinIt->targetField ;
639
+ }
640
+ }
641
+
626
642
if ( !mFetchJoinInfo .isEmpty () )
627
643
addJoinedAttributes ( f );
628
644
645
+ // add remaining expression fields
629
646
if ( !mExpressionFieldInfo .isEmpty () )
630
647
{
631
648
QMap<int , QgsExpression*>::ConstIterator it = mExpressionFieldInfo .constBegin ();
632
-
633
649
for ( ; it != mExpressionFieldInfo .constEnd (); ++it )
634
650
{
635
- QgsExpression* exp = it.value ();
636
- mExpressionContext ->setFeature ( f );
637
- QVariant val = exp ->evaluate ( mExpressionContext .data () );
638
- mSource ->mFields .at ( it.key () ).convertCompatible ( val );
639
- f.setAttribute ( it.key (), val );
651
+ if ( fetchedVirtualAttributes.contains ( it.key () ) )
652
+ continue ;
653
+
654
+ addExpressionAttribute ( f, it.key () );
640
655
}
641
656
}
642
657
}
643
658
659
+ void QgsVectorLayerFeatureIterator::addExpressionAttribute ( QgsFeature& f, int attrIndex )
660
+ {
661
+ QgsExpression* exp = mExpressionFieldInfo .value ( attrIndex );
662
+ mExpressionContext ->setFeature ( f );
663
+ QVariant val = exp ->evaluate ( mExpressionContext .data () );
664
+ mSource ->mFields .at ( attrIndex ).convertCompatible ( val );
665
+ f.setAttribute ( attrIndex, val );
666
+ }
667
+
644
668
bool QgsVectorLayerFeatureIterator::prepareSimplification ( const QgsSimplifyMethod& simplifyMethod )
645
669
{
646
670
delete mEditGeometrySimplifier ;
0 commit comments