Push minimumValues/maximumValues up to QgsFeatureSource base class

Allows these methods to be called on feature sources
This commit is contained in:
Nyall Dawson 2017-07-13 20:03:31 +10:00
parent cfbed91103
commit eb0c3015f9
13 changed files with 205 additions and 6 deletions

View File

@ -232,6 +232,12 @@ class QgsProcessingFeatureSource : QgsFeatureSource
virtual QString sourceName() const;
virtual QSet<QVariant> uniqueValues( int fieldIndex, int limit = -1 ) const;
virtual QVariant minimumValue( int fieldIndex ) const;
virtual QVariant maximumValue( int fieldIndex ) const;
};

View File

@ -81,9 +81,31 @@ class QgsFeatureSource
If specified, the ``limit`` option can be used to limit the number of returned values.
The base class implementation uses a non-optimised approach of looping through
all features in the source.
.. seealso:: minimumValue()
.. seealso:: maximumValue()
:rtype: set of QVariant
%End
virtual QVariant minimumValue( int fieldIndex ) const;
%Docstring
Returns the minimum value for an attribute column or an invalid variant in case of error.
The base class implementation uses a non-optimised approach of looping through
all features in the source.
.. seealso:: maximumValue()
.. seealso:: uniqueValues()
:rtype: QVariant
%End
virtual QVariant maximumValue( int fieldIndex ) const;
%Docstring
Returns the maximum value for an attribute column or an invalid variant in case of error.
The base class implementation uses a non-optimised approach of looping through
all features in the source.
.. seealso:: minimumValue()
.. seealso:: uniqueValues()
:rtype: QVariant
%End
virtual QgsRectangle sourceExtent() const;
%Docstring
Returns the extent of all geometries from the source.

View File

@ -1537,7 +1537,8 @@ Assembles mUpdatedFields considering provider fields, joined fields and added fi
:rtype: list of str
%End
QVariant minimumValue( int index ) const;
virtual QVariant minimumValue( int index ) const;
%Docstring
Returns the minimum value for an attribute column or an invalid variant in case of error.
Note that in some circumstances when unsaved changes are present for the layer then the
@ -1548,7 +1549,8 @@ Assembles mUpdatedFields considering provider fields, joined fields and added fi
:rtype: QVariant
%End
QVariant maximumValue( int index ) const;
virtual QVariant maximumValue( int index ) const;
%Docstring
Returns the maximum value for an attribute column or an invalid variant in case of error.
Note that in some circumstances when unsaved changes are present for the layer then the

View File

@ -566,3 +566,18 @@ QString QgsProcessingFeatureSource::sourceName() const
return mSource->sourceName();
}
QSet<QVariant> QgsProcessingFeatureSource::uniqueValues( int fieldIndex, int limit ) const
{
return mSource->uniqueValues( fieldIndex, limit );
}
QVariant QgsProcessingFeatureSource::minimumValue( int fieldIndex ) const
{
return mSource->minimumValue( fieldIndex );
}
QVariant QgsProcessingFeatureSource::maximumValue( int fieldIndex ) const
{
return mSource->maximumValue( fieldIndex );
}

View File

@ -282,6 +282,9 @@ class CORE_EXPORT QgsProcessingFeatureSource : public QgsFeatureSource
QgsWkbTypes::Type wkbType() const override;
long featureCount() const override;
QString sourceName() const override;
QSet<QVariant> uniqueValues( int fieldIndex, int limit = -1 ) const override;
QVariant minimumValue( int fieldIndex ) const override;
QVariant maximumValue( int fieldIndex ) const override;
private:

View File

@ -40,6 +40,52 @@ QSet<QVariant> QgsFeatureSource::uniqueValues( int fieldIndex, int limit ) const
return values;
}
QVariant QgsFeatureSource::minimumValue( int fieldIndex ) const
{
if ( fieldIndex < 0 || fieldIndex >= fields().count() )
return QVariant();
QgsFeatureRequest req;
req.setFlags( QgsFeatureRequest::NoGeometry );
req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
QVariant min;
QgsFeatureIterator it = getFeatures( req );
QgsFeature f;
while ( it.nextFeature( f ) )
{
QVariant v = f.attribute( fieldIndex );
if ( v.isValid() && qgsVariantLessThan( v, min ) )
{
min = v;
}
}
return min;
}
QVariant QgsFeatureSource::maximumValue( int fieldIndex ) const
{
if ( fieldIndex < 0 || fieldIndex >= fields().count() )
return QVariant();
QgsFeatureRequest req;
req.setFlags( QgsFeatureRequest::NoGeometry );
req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
QVariant max;
QgsFeatureIterator it = getFeatures( req );
QgsFeature f;
while ( it.nextFeature( f ) )
{
QVariant v = f.attribute( fieldIndex );
if ( v.isValid() && qgsVariantGreaterThan( v, max ) )
{
max = v;
}
}
return max;
}
QgsRectangle QgsFeatureSource::sourceExtent() const
{
QgsRectangle r;

View File

@ -89,9 +89,27 @@ class CORE_EXPORT QgsFeatureSource
* If specified, the \a limit option can be used to limit the number of returned values.
* The base class implementation uses a non-optimised approach of looping through
* all features in the source.
* \see minimumValue()
* \see maximumValue()
*/
virtual QSet<QVariant> uniqueValues( int fieldIndex, int limit = -1 ) const;
/** Returns the minimum value for an attribute column or an invalid variant in case of error.
* The base class implementation uses a non-optimised approach of looping through
* all features in the source.
* \see maximumValue()
* \see uniqueValues()
*/
virtual QVariant minimumValue( int fieldIndex ) const;
/** Returns the maximum value for an attribute column or an invalid variant in case of error.
* The base class implementation uses a non-optimised approach of looping through
* all features in the source.
* \see minimumValue()
* \see uniqueValues()
*/
virtual QVariant maximumValue( int fieldIndex ) const;
/**
* Returns the extent of all geometries from the source.
* The base class implementation uses a non-optimised approach of looping through

View File

@ -177,7 +177,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
* and maximal values. If provider has facilities to retrieve minimal
* value directly, override this function.
*/
virtual QVariant minimumValue( int index ) const;
virtual QVariant minimumValue( int index ) const override;
/**
* Returns the maximum value of an attribute
@ -187,7 +187,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
* and maximal values. If provider has facilities to retrieve maximal
* value directly, override this function.
*/
virtual QVariant maximumValue( int index ) const;
virtual QVariant maximumValue( int index ) const override;
/**
* Returns unique string values of an attribute which contain a specified subset string. Subset

View File

@ -1452,7 +1452,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* \see maximumValue()
* \see uniqueValues()
*/
QVariant minimumValue( int index ) const;
QVariant minimumValue( int index ) const override;
/** Returns the maximum value for an attribute column or an invalid variant in case of error.
* Note that in some circumstances when unsaved changes are present for the layer then the
@ -1461,7 +1461,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* \see minimumValue()
* \see uniqueValues()
*/
QVariant maximumValue( int index ) const;
QVariant maximumValue( int index ) const override;
/** Calculates an aggregated value from the layer's features.
* \param aggregate aggregate to calculate

View File

@ -635,3 +635,15 @@ class FeatureSourceTestCase(object):
assert f.hasGeometry(), 'Expected geometry, got none'
self.assertTrue(f.isValid())
def testUniqueValues(self):
self.assertEqual(set(self.source.uniqueValues(1)), set([-200, 100, 200, 300, 400]))
assert set(['Apple', 'Honey', 'Orange', 'Pear', NULL]) == set(self.source.uniqueValues(2)), 'Got {}'.format(set(self.source.uniqueValues(2)))
def testMinimumValue(self):
self.assertEqual(self.source.minimumValue(1), -200)
self.assertEqual(self.source.minimumValue(2), 'Apple')
def testMaximumValue(self):
self.assertEqual(self.source.maximumValue(1), 400)
self.assertEqual(self.source.maximumValue(2), 'Pear')

View File

@ -62,6 +62,30 @@ class TestQgsFeatureSource(unittest.TestCase):
self.assertEqual(layer.dataProvider().uniqueValues(0), {'test', 'test2', 'test3', 'test4'})
self.assertEqual(layer.dataProvider().uniqueValues(1), {1, 3, 3, 4})
def testMinValues(self):
"""
Test retrieving min values using base class method
"""
# memory provider uses base class method
layer = createLayerWithFivePoints()
self.assertFalse(layer.dataProvider().minimumValue(-1))
self.assertFalse(layer.dataProvider().minimumValue(100))
self.assertEqual(layer.dataProvider().minimumValue(0), 'test')
self.assertEqual(layer.dataProvider().minimumValue(1), 1)
def testMaxValues(self):
"""
Test retrieving min values using base class method
"""
# memory provider uses base class method
layer = createLayerWithFivePoints()
self.assertFalse(layer.dataProvider().maximumValue(-1))
self.assertFalse(layer.dataProvider().maximumValue(100))
self.assertEqual(layer.dataProvider().maximumValue(0), 'test4')
self.assertEqual(layer.dataProvider().maximumValue(1), 4)
if __name__ == '__main__':
unittest.main()

View File

@ -2511,6 +2511,12 @@ class TestQgsVectorLayerSourceAddedFeaturesInBuffer(unittest.TestCase, FeatureSo
"""
pass
def testMinimumValue(self):
""" Skip min values test - due to inconsistencies in how null values are treated by providers.
They are included here, but providers don't include them.... which is right?
"""
pass
class TestQgsVectorLayerSourceChangedGeometriesInBuffer(unittest.TestCase, FeatureSourceTestCase):
@ -2661,6 +2667,21 @@ class TestQgsVectorLayerSourceChangedAttributesInBuffer(unittest.TestCase, Featu
"""
pass
def testUniqueValues(self):
""" Skip unique values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
def testMinimumValue(self):
""" Skip min values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
def testMaximumValue(self):
""" Skip max values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
class TestQgsVectorLayerSourceDeletedFeaturesInBuffer(unittest.TestCase, FeatureSourceTestCase):
@ -2746,6 +2767,21 @@ class TestQgsVectorLayerSourceDeletedFeaturesInBuffer(unittest.TestCase, Feature
"""
pass
def testUniqueValues(self):
""" Skip unique values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
def testMinimumValue(self):
""" Skip min values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
def testMaximumValue(self):
""" Skip max values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
# TODO:
# - fetch rect: feat with changed geometry: 1. in rect, 2. out of rect
# - more join tests

View File

@ -90,6 +90,21 @@ class TestQgsVectorLayerCache(unittest.TestCase, FeatureSourceTestCase):
"""
pass
def testUniqueValues(self):
""" Skip unique values test - not implemented by the cache (yet)
"""
pass
def testMinimumValue(self):
""" Skip min values test - not implemented by the cache (yet)
"""
pass
def testMaximumValue(self):
""" Skip max values test - not implemented by the cache (yet)
"""
pass
if __name__ == '__main__':
unittest.main()