mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Add possibility to handle aggregate calculation at data provider
(not implemented for any providers yet)
This commit is contained in:
parent
50e41c8133
commit
dcc047af49
@ -1,7 +1,9 @@
|
||||
/** \ingroup core
|
||||
* \class QgsAggregateCalculator
|
||||
* \brief Utility class for calculating aggregates for a field (or expression) over the features
|
||||
* from a vector layer.
|
||||
* from a vector layer. It is recommended that QgsVectorLayer::aggregate() is used rather then
|
||||
* directly using this class, as the QgsVectorLayer method can handle delegating aggregate calculation
|
||||
* to a data provider for remote calculation.
|
||||
* \note added in QGIS 2.16
|
||||
*/
|
||||
class QgsAggregateCalculator
|
||||
|
@ -154,6 +154,23 @@ class QgsVectorDataProvider : QgsDataProvider
|
||||
*/
|
||||
virtual void uniqueValues( int index, QList<QVariant> &uniqueValues /Out/, int limit = -1 );
|
||||
|
||||
/** Calculates an aggregated value from the layer's features. The base implementation does nothing,
|
||||
* but subclasses can override this method to handoff calculation of aggregates to the provider.
|
||||
* @param aggregate aggregate to calculate
|
||||
* @param index the index of the attribute to calculate aggregate over
|
||||
* @param filter optional filter for calculating aggregate over a subset of features, or an
|
||||
* empty string to use all features
|
||||
* @param context expression context for filter
|
||||
* @param ok will be set to true if calculation was successfully performed by the data provider
|
||||
* @return calculated aggregate value
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
virtual QVariant aggregate( QgsAggregateCalculator::Aggregate aggregate,
|
||||
int index,
|
||||
const QString& filter,
|
||||
QgsExpressionContext* context,
|
||||
bool& ok );
|
||||
|
||||
/**
|
||||
* Returns the possible enum values of an attribute. Returns an empty stringlist if a provider does not support enum types
|
||||
* or if the given attribute is not an enum type.
|
||||
|
@ -1275,6 +1275,22 @@ class QgsVectorLayer : QgsMapLayer
|
||||
/** Returns maximum value for an attribute column or invalid variant in case of error */
|
||||
QVariant maximumValue( int index );
|
||||
|
||||
/** Calculates an aggregated value from the layer's features.
|
||||
* @param aggregate aggregate to calculate
|
||||
* @param fieldOrExpression source field or expression to use as basis for aggregated values.
|
||||
* @param filter optional filter for calculating aggregate over a subset of features, or an
|
||||
* empty string to use all features
|
||||
* @param context expression context for expressions and filters
|
||||
* @param ok if specified, will be set to true if aggregate calculation was successful
|
||||
* @return calculated aggregate value
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
QVariant aggregate( QgsAggregateCalculator::Aggregate aggregate,
|
||||
const QString& fieldOrExpression,
|
||||
const QString& filter = QString(),
|
||||
QgsExpressionContext* context = nullptr,
|
||||
bool* ok = nullptr );
|
||||
|
||||
/** Fetches all values from a specified field name or expression.
|
||||
* @param fieldOrExpression field name or an expression string
|
||||
* @param ok will be set to false if field or expression is invalid, otherwise true
|
||||
|
@ -101,8 +101,16 @@ QVariant QgsAggregateCalculator::calculate( QgsAggregateCalculator::Aggregate ag
|
||||
resultType = mLayer->fields().at( attrNum ).type();
|
||||
}
|
||||
|
||||
|
||||
QgsFeatureIterator fit = mLayer->getFeatures( request );
|
||||
return calculate( aggregate, fit, resultType, attrNum, expression.data(), context, ok );
|
||||
}
|
||||
|
||||
QVariant QgsAggregateCalculator::calculate( QgsAggregateCalculator::Aggregate aggregate, QgsFeatureIterator& fit, QVariant::Type resultType,
|
||||
int attr, QgsExpression* expression, QgsExpressionContext* context, bool* ok )
|
||||
{
|
||||
if ( ok )
|
||||
*ok = false;
|
||||
|
||||
switch ( resultType )
|
||||
{
|
||||
case QVariant::Int:
|
||||
@ -111,32 +119,40 @@ QVariant QgsAggregateCalculator::calculate( QgsAggregateCalculator::Aggregate ag
|
||||
case QVariant::ULongLong:
|
||||
case QVariant::Double:
|
||||
{
|
||||
QgsStatisticalSummary::Statistic stat = numericStatFromAggregate( aggregate, ok );
|
||||
if ( !ok )
|
||||
bool statOk = false;
|
||||
QgsStatisticalSummary::Statistic stat = numericStatFromAggregate( aggregate, &statOk );
|
||||
if ( !statOk )
|
||||
return QVariant();
|
||||
|
||||
|
||||
return calculateNumericAggregate( fit, attrNum, expression.data(), context, stat );
|
||||
if ( ok )
|
||||
*ok = true;
|
||||
return calculateNumericAggregate( fit, attr, expression, context, stat );
|
||||
}
|
||||
|
||||
case QVariant::Date:
|
||||
case QVariant::DateTime:
|
||||
{
|
||||
QgsDateTimeStatisticalSummary::Statistic stat = dateTimeStatFromAggregate( aggregate, ok );
|
||||
if ( !ok )
|
||||
bool statOk = false;
|
||||
QgsDateTimeStatisticalSummary::Statistic stat = dateTimeStatFromAggregate( aggregate, &statOk );
|
||||
if ( !statOk )
|
||||
return QVariant();
|
||||
|
||||
return calculateDateTimeAggregate( fit, attrNum, expression.data(), context, stat );
|
||||
if ( ok )
|
||||
*ok = true;
|
||||
return calculateDateTimeAggregate( fit, attr, expression, context, stat );
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// treat as string
|
||||
QgsStringStatisticalSummary::Statistic stat = stringStatFromAggregate( aggregate, ok );
|
||||
if ( !ok )
|
||||
bool statOk = false;
|
||||
QgsStringStatisticalSummary::Statistic stat = stringStatFromAggregate( aggregate, &statOk );
|
||||
if ( !statOk )
|
||||
return QVariant();
|
||||
|
||||
return calculateStringAggregate( fit, attrNum, expression.data(), context, stat );
|
||||
if ( ok )
|
||||
*ok = true;
|
||||
return calculateStringAggregate( fit, attr, expression, context, stat );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include "qgsstatisticalsummary.h"
|
||||
#include "qgsdatetimestatisticalsummary.h"
|
||||
#include "qgsstringstatisticalsummary.h"
|
||||
#include <QVariant>
|
||||
|
||||
|
||||
class QgsFeatureIterator;
|
||||
class QgsExpression;
|
||||
@ -30,7 +32,9 @@ class QgsExpressionContext;
|
||||
/** \ingroup core
|
||||
* \class QgsAggregateCalculator
|
||||
* \brief Utility class for calculating aggregates for a field (or expression) over the features
|
||||
* from a vector layer.
|
||||
* from a vector layer. It is recommended that QgsVectorLayer::aggregate() is used rather then
|
||||
* directly using this class, as the QgsVectorLayer method can handle delegating aggregate calculation
|
||||
* to a data provider for remote calculation.
|
||||
* \note added in QGIS 2.16
|
||||
*/
|
||||
class CORE_EXPORT QgsAggregateCalculator
|
||||
@ -114,6 +118,10 @@ class CORE_EXPORT QgsAggregateCalculator
|
||||
QgsExpressionContext* context, QgsDateTimeStatisticalSummary::Statistic stat );
|
||||
|
||||
QgsExpressionContext* createContext() const;
|
||||
|
||||
static QVariant calculate( Aggregate aggregate, QgsFeatureIterator& fit, QVariant::Type resultType,
|
||||
int attr, QgsExpression* expression,
|
||||
QgsExpressionContext* context, bool* ok = nullptr );
|
||||
};
|
||||
|
||||
#endif //QGSAGGREGATECALCULATOR_H
|
||||
|
@ -144,6 +144,7 @@ class CORE_EXPORT QgsStatisticalSummary
|
||||
int count() const { return mCount; }
|
||||
|
||||
/** Returns the number of missing (null) values
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
int countMissing() const { return mMissing; }
|
||||
|
||||
|
@ -381,6 +381,19 @@ void QgsVectorDataProvider::uniqueValues( int index, QList<QVariant> &values, in
|
||||
}
|
||||
}
|
||||
|
||||
QVariant QgsVectorDataProvider::aggregate( QgsAggregateCalculator::Aggregate aggregate, int index,
|
||||
const QString& filter, QgsExpressionContext* context, bool& ok )
|
||||
{
|
||||
//base implementation does nothing
|
||||
Q_UNUSED( aggregate );
|
||||
Q_UNUSED( index );
|
||||
Q_UNUSED( filter );
|
||||
Q_UNUSED( context );
|
||||
|
||||
ok = false;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void QgsVectorDataProvider::clearMinMaxCache()
|
||||
{
|
||||
mCacheMinMaxDirty = true;
|
||||
|
@ -28,6 +28,7 @@ class QTextCodec;
|
||||
#include "qgsfeature.h"
|
||||
#include "qgsfield.h"
|
||||
#include "qgsrectangle.h"
|
||||
#include "qgsaggregatecalculator.h"
|
||||
|
||||
typedef QList<int> QgsAttributeList;
|
||||
typedef QSet<int> QgsAttributeIds;
|
||||
@ -202,6 +203,23 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
|
||||
*/
|
||||
virtual void uniqueValues( int index, QList<QVariant> &uniqueValues, int limit = -1 );
|
||||
|
||||
/** Calculates an aggregated value from the layer's features. The base implementation does nothing,
|
||||
* but subclasses can override this method to handoff calculation of aggregates to the provider.
|
||||
* @param aggregate aggregate to calculate
|
||||
* @param index the index of the attribute to calculate aggregate over
|
||||
* @param filter optional filter for calculating aggregate over a subset of features, or an
|
||||
* empty string to use all features
|
||||
* @param context expression context for filter
|
||||
* @param ok will be set to true if calculation was successfully performed by the data provider
|
||||
* @return calculated aggregate value
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
virtual QVariant aggregate( QgsAggregateCalculator::Aggregate aggregate,
|
||||
int index,
|
||||
const QString& filter,
|
||||
QgsExpressionContext* context,
|
||||
bool& ok );
|
||||
|
||||
/**
|
||||
* Returns the possible enum values of an attribute. Returns an empty stringlist if a provider does not support enum types
|
||||
* or if the given attribute is not an enum type.
|
||||
|
@ -3254,6 +3254,44 @@ QVariant QgsVectorLayer::maximumValue( int index )
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant QgsVectorLayer::aggregate( QgsAggregateCalculator::Aggregate aggregate, const QString& fieldOrExpression,
|
||||
const QString& filter, QgsExpressionContext* context, bool* ok )
|
||||
{
|
||||
if ( ok )
|
||||
*ok = false;
|
||||
|
||||
if ( !mDataProvider )
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
// test if we are calculating based on a field
|
||||
int attrIndex = mUpdatedFields.fieldNameIndex( fieldOrExpression );
|
||||
if ( attrIndex >= 0 )
|
||||
{
|
||||
// aggregate is based on a field - if it's a provider field, we could possibly hand over the calculation
|
||||
// to the provider itself
|
||||
QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( attrIndex );
|
||||
if ( origin == QgsFields::OriginProvider )
|
||||
{
|
||||
bool providerOk = false;
|
||||
QVariant val = mDataProvider->aggregate( aggregate, attrIndex, filter, context, providerOk );
|
||||
if ( providerOk )
|
||||
{
|
||||
// provider handled calculation
|
||||
if ( ok )
|
||||
*ok = true;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to using aggregate calculator to determine aggregate
|
||||
QgsAggregateCalculator c( this );
|
||||
c.setFilter( filter );
|
||||
return c.calculate( aggregate, fieldOrExpression, context, ok );
|
||||
}
|
||||
|
||||
QList<QVariant> QgsVectorLayer::getValues( const QString &fieldOrExpression, bool& ok, bool selectedOnly )
|
||||
{
|
||||
QList<QVariant> values;
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "qgsvectorsimplifymethod.h"
|
||||
#include "qgseditformconfig.h"
|
||||
#include "qgsattributetableconfig.h"
|
||||
#include "qgsaggregatecalculator.h"
|
||||
|
||||
class QPainter;
|
||||
class QImage;
|
||||
@ -1656,6 +1657,22 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
|
||||
/** Returns maximum value for an attribute column or invalid variant in case of error */
|
||||
QVariant maximumValue( int index );
|
||||
|
||||
/** Calculates an aggregated value from the layer's features.
|
||||
* @param aggregate aggregate to calculate
|
||||
* @param fieldOrExpression source field or expression to use as basis for aggregated values.
|
||||
* @param filter optional filter for calculating aggregate over a subset of features, or an
|
||||
* empty string to use all features
|
||||
* @param context expression context for expressions and filters
|
||||
* @param ok if specified, will be set to true if aggregate calculation was successful
|
||||
* @return calculated aggregate value
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
QVariant aggregate( QgsAggregateCalculator::Aggregate aggregate,
|
||||
const QString& fieldOrExpression,
|
||||
const QString& filter = QString(),
|
||||
QgsExpressionContext* context = nullptr,
|
||||
bool* ok = nullptr );
|
||||
|
||||
/** Fetches all values from a specified field name or expression.
|
||||
* @param fieldOrExpression field name or an expression string
|
||||
* @param ok will be set to false if field or expression is invalid, otherwise true
|
||||
|
@ -33,7 +33,8 @@ from qgis.core import (QGis,
|
||||
QgsSingleSymbolRendererV2,
|
||||
QgsCoordinateReferenceSystem,
|
||||
QgsProject,
|
||||
QgsUnitTypes)
|
||||
QgsUnitTypes,
|
||||
QgsAggregateCalculator)
|
||||
from qgis.testing import start_app, unittest
|
||||
from utilities import unitTestDataPath
|
||||
start_app()
|
||||
@ -1062,6 +1063,44 @@ class TestQgsVectorLayer(unittest.TestCase):
|
||||
|
||||
assert(len(list(features)) == 1)
|
||||
|
||||
def testAggregate(self):
|
||||
""" Test aggregate calculation """
|
||||
layer = QgsVectorLayer("Point?field=fldint:integer", "layer", "memory")
|
||||
pr = layer.dataProvider()
|
||||
|
||||
int_values = [4, 2, 3, 2, 5, None, 8]
|
||||
features = []
|
||||
for i in int_values:
|
||||
f = QgsFeature()
|
||||
f.setFields(layer.fields())
|
||||
f.setAttributes([i])
|
||||
features.append(f)
|
||||
assert pr.addFeatures(features)
|
||||
|
||||
tests = [[QgsAggregateCalculator.Count, 6],
|
||||
[QgsAggregateCalculator.Sum, 24],
|
||||
[QgsAggregateCalculator.Mean, 4],
|
||||
[QgsAggregateCalculator.StDev, 2.0816],
|
||||
[QgsAggregateCalculator.StDevSample, 2.2803],
|
||||
[QgsAggregateCalculator.Min, 2],
|
||||
[QgsAggregateCalculator.Max, 8],
|
||||
[QgsAggregateCalculator.Range, 6],
|
||||
[QgsAggregateCalculator.Median, 3.5],
|
||||
[QgsAggregateCalculator.CountDistinct, 5],
|
||||
[QgsAggregateCalculator.CountMissing, 1],
|
||||
[QgsAggregateCalculator.FirstQuartile, 2],
|
||||
[QgsAggregateCalculator.ThirdQuartile, 5.0],
|
||||
[QgsAggregateCalculator.InterQuartileRange, 3.0]
|
||||
]
|
||||
|
||||
for t in tests:
|
||||
val, ok = layer.aggregate(t[0], 'fldint')
|
||||
self.assertTrue(ok)
|
||||
if isinstance(t[1], int):
|
||||
self.assertEqual(val, t[1])
|
||||
else:
|
||||
self.assertAlmostEqual(val, t[1], 3)
|
||||
|
||||
def onLayerTransparencyChanged(self, tr):
|
||||
self.transparencyTest = tr
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user