Classifications on joined fields should only consider values which

are matched to layer's features (fix #9051)
This commit is contained in:
Nyall Dawson 2016-06-13 13:43:46 +10:00
parent cb4dbeafd1
commit 16eb1e14d0
2 changed files with 172 additions and 154 deletions

View File

@ -3136,12 +3136,12 @@ void QgsVectorLayer::uniqueValues( int index, QList<QVariant> &uniqueValues, int
} }
QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index ); QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index );
if ( origin == QgsFields::OriginUnknown ) switch ( origin )
{ {
case QgsFields::OriginUnknown:
return; return;
}
if ( origin == QgsFields::OriginProvider ) //a provider field case QgsFields::OriginProvider: //a provider field
{ {
mDataProvider->uniqueValues( index, uniqueValues, limit ); mDataProvider->uniqueValues( index, uniqueValues, limit );
@ -3172,29 +3172,22 @@ void QgsVectorLayer::uniqueValues( int index, QList<QVariant> &uniqueValues, int
return; return;
} }
else if ( origin == QgsFields::OriginJoin )
{
int sourceLayerIndex;
const QgsVectorJoinInfo* join = mJoinBuffer->joinForFieldIndex( index, mUpdatedFields, sourceLayerIndex );
Q_ASSERT( join );
QgsVectorLayer *vl = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( join->joinLayerId ) ); case QgsFields::OriginEdit:
if ( vl )
vl->dataProvider()->uniqueValues( sourceLayerIndex, uniqueValues, limit );
return;
}
else if ( origin == QgsFields::OriginEdit || origin == QgsFields::OriginExpression )
{
// the layer is editable, but in certain cases it can still be avoided going through all features // the layer is editable, but in certain cases it can still be avoided going through all features
if ( origin == QgsFields::OriginEdit && mEditBuffer->mDeletedFeatureIds.isEmpty() && mEditBuffer->mAddedFeatures.isEmpty() && !mEditBuffer->mDeletedAttributeIds.contains( index ) && mEditBuffer->mChangedAttributeValues.isEmpty() ) if ( mEditBuffer->mDeletedFeatureIds.isEmpty() &&
mEditBuffer->mAddedFeatures.isEmpty() &&
!mEditBuffer->mDeletedAttributeIds.contains( index ) &&
mEditBuffer->mChangedAttributeValues.isEmpty() )
{ {
mDataProvider->uniqueValues( index, uniqueValues, limit ); mDataProvider->uniqueValues( index, uniqueValues, limit );
return; return;
} }
FALLTHROUGH
//we need to go through each feature //we need to go through each feature
case QgsFields::OriginJoin:
case QgsFields::OriginExpression:
{
QgsAttributeList attList; QgsAttributeList attList;
attList << index; attList << index;
@ -3218,6 +3211,7 @@ void QgsVectorLayer::uniqueValues( int index, QList<QVariant> &uniqueValues, int
uniqueValues = val.values(); uniqueValues = val.values();
return; return;
} }
}
Q_ASSERT_X( false, "QgsVectorLayer::uniqueValues()", "Unknown source of the field!" ); Q_ASSERT_X( false, "QgsVectorLayer::uniqueValues()", "Unknown source of the field!" );
} }
@ -3230,38 +3224,31 @@ QVariant QgsVectorLayer::minimumValue( int index )
} }
QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index ); QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index );
if ( origin == QgsFields::OriginUnknown )
switch ( origin )
{ {
case QgsFields::OriginUnknown:
return QVariant(); return QVariant();
}
if ( origin == QgsFields::OriginProvider ) //a provider field case QgsFields::OriginProvider: //a provider field
{
return mDataProvider->minimumValue( index ); return mDataProvider->minimumValue( index );
}
else if ( origin == QgsFields::OriginJoin )
{
int sourceLayerIndex;
const QgsVectorJoinInfo* join = mJoinBuffer->joinForFieldIndex( index, mUpdatedFields, sourceLayerIndex );
Q_ASSERT( join );
QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( join->joinLayerId ) ); case QgsFields::OriginEdit:
Q_ASSERT( vl );
return vl->minimumValue( sourceLayerIndex );
}
else if ( origin == QgsFields::OriginEdit || origin == QgsFields::OriginExpression )
{ {
// the layer is editable, but in certain cases it can still be avoided going through all features // the layer is editable, but in certain cases it can still be avoided going through all features
if ( origin == QgsFields::OriginEdit && if ( mEditBuffer->mDeletedFeatureIds.isEmpty() &&
mEditBuffer->mDeletedFeatureIds.isEmpty() &&
mEditBuffer->mAddedFeatures.isEmpty() && ! mEditBuffer->mAddedFeatures.isEmpty() && !
mEditBuffer->mDeletedAttributeIds.contains( index ) && mEditBuffer->mDeletedAttributeIds.contains( index ) &&
mEditBuffer->mChangedAttributeValues.isEmpty() ) mEditBuffer->mChangedAttributeValues.isEmpty() )
{ {
return mDataProvider->minimumValue( index ); return mDataProvider->minimumValue( index );
} }
}
FALLTHROUGH
// no choice but to go through all features
case QgsFields::OriginExpression:
case QgsFields::OriginJoin:
{
// we need to go through each feature // we need to go through each feature
QgsAttributeList attList; QgsAttributeList attList;
attList << index; attList << index;
@ -3283,6 +3270,7 @@ QVariant QgsVectorLayer::minimumValue( int index )
} }
return QVariant( minimumValue ); return QVariant( minimumValue );
} }
}
Q_ASSERT_X( false, "QgsVectorLayer::minimumValue()", "Unknown source of the field!" ); Q_ASSERT_X( false, "QgsVectorLayer::minimumValue()", "Unknown source of the field!" );
return QVariant(); return QVariant();
@ -3296,31 +3284,17 @@ QVariant QgsVectorLayer::maximumValue( int index )
} }
QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index ); QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index );
if ( origin == QgsFields::OriginUnknown ) switch ( origin )
{ {
case QgsFields::OriginUnknown:
return QVariant(); return QVariant();
}
if ( origin == QgsFields::OriginProvider ) //a provider field case QgsFields::OriginProvider: //a provider field
{
return mDataProvider->maximumValue( index ); return mDataProvider->maximumValue( index );
}
else if ( origin == QgsFields::OriginJoin )
{
int sourceLayerIndex;
const QgsVectorJoinInfo* join = mJoinBuffer->joinForFieldIndex( index, mUpdatedFields, sourceLayerIndex );
Q_ASSERT( join );
QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( join->joinLayerId ) ); case QgsFields::OriginEdit:
Q_ASSERT( vl );
return vl->maximumValue( sourceLayerIndex );
}
else if ( origin == QgsFields::OriginEdit || origin == QgsFields::OriginExpression )
{
// the layer is editable, but in certain cases it can still be avoided going through all features // the layer is editable, but in certain cases it can still be avoided going through all features
if ( origin == QgsFields::OriginEdit && if ( mEditBuffer->mDeletedFeatureIds.isEmpty() &&
mEditBuffer->mDeletedFeatureIds.isEmpty() &&
mEditBuffer->mAddedFeatures.isEmpty() && mEditBuffer->mAddedFeatures.isEmpty() &&
!mEditBuffer->mDeletedAttributeIds.contains( index ) && !mEditBuffer->mDeletedAttributeIds.contains( index ) &&
mEditBuffer->mChangedAttributeValues.isEmpty() ) mEditBuffer->mChangedAttributeValues.isEmpty() )
@ -3328,7 +3302,11 @@ QVariant QgsVectorLayer::maximumValue( int index )
return mDataProvider->maximumValue( index ); return mDataProvider->maximumValue( index );
} }
// we need to go through each feature FALLTHROUGH
//no choice but to go through each feature
case QgsFields::OriginJoin:
case QgsFields::OriginExpression:
{
QgsAttributeList attList; QgsAttributeList attList;
attList << index; attList << index;
@ -3349,6 +3327,7 @@ QVariant QgsVectorLayer::maximumValue( int index )
} }
return QVariant( maximumValue ); return QVariant( maximumValue );
} }
}
Q_ASSERT_X( false, "QgsVectorLayer::maximumValue()", "Unknown source of the field!" ); Q_ASSERT_X( false, "QgsVectorLayer::maximumValue()", "Unknown source of the field!" );
return QVariant(); return QVariant();

View File

@ -59,6 +59,21 @@ def createLayerWithOnePoint():
return layer return layer
def createLayerWithTwoPoints():
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
"addfeat", "memory")
pr = layer.dataProvider()
f = QgsFeature()
f.setAttributes(["test", 123])
f.setGeometry(QgsGeometry.fromPoint(QgsPoint(100, 200)))
f2 = QgsFeature()
f2.setAttributes(["test2", 457])
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(100, 200)))
assert pr.addFeatures([f, f2])
assert layer.pendingFeatureCount() == 2
return layer
def createJoinLayer(): def createJoinLayer():
joinLayer = QgsVectorLayer( joinLayer = QgsVectorLayer(
"Point?field=x:string&field=y:integer&field=z:integer", "Point?field=x:string&field=y:integer&field=z:integer",
@ -70,8 +85,14 @@ def createJoinLayer():
f2 = QgsFeature() f2 = QgsFeature()
f2.setAttributes(["bar", 456, 654]) f2.setAttributes(["bar", 456, 654])
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 2))) f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 2)))
assert pr.addFeatures([f1, f2]) f3 = QgsFeature()
assert joinLayer.pendingFeatureCount() == 2 f3.setAttributes(["qar", 457, 111])
f3.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 2)))
f4 = QgsFeature()
f4.setAttributes(["a", 458, 19])
f4.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 2)))
assert pr.addFeatures([f1, f2, f3, f4])
assert joinLayer.pendingFeatureCount() == 4
return joinLayer return joinLayer
@ -1083,6 +1104,24 @@ class TestQgsVectorLayer(unittest.TestCase):
self.assertEqual(f2[2], "foo") self.assertEqual(f2[2], "foo")
self.assertEqual(f2[3], 321) self.assertEqual(f2[3], 321)
def test_JoinStats(self):
""" test calculating min/max/uniqueValues on joined field """
joinLayer = createJoinLayer()
layer = createLayerWithTwoPoints()
QgsMapLayerRegistry.instance().addMapLayers([joinLayer, layer])
join = QgsVectorJoinInfo()
join.targetFieldName = "fldint"
join.joinLayerId = joinLayer.id()
join.joinFieldName = "y"
join.memoryCache = True
layer.addJoin(join)
# stats on joined fields should only include values present by join
self.assertEqual(layer.minimumValue(3), 111)
self.assertEqual(layer.maximumValue(3), 321)
self.assertEqual(set(layer.uniqueValues(3)), set([111, 321]))
def test_InvalidOperations(self): def test_InvalidOperations(self):
layer = createLayerWithOnePoint() layer = createLayerWithOnePoint()