QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgsaggregatecalculator.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsaggregatecalculator.cpp
3  --------------------------
4  begin : May 2016
5  copyright : (C) 2016 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsaggregatecalculator.h"
19 #include "qgsfeature.h"
20 #include "qgsfeaturerequest.h"
21 #include "qgsfeatureiterator.h"
22 #include "qgsvectorlayer.h"
23 
24 
25 
27  : mLayer( layer )
28 {
29 
30 }
31 
33 {
34  return mLayer;
35 }
36 
38 {
39  mFilterExpression = parameters.filter;
40  mDelimiter = parameters.delimiter;
41 }
42 
44  const QString& fieldOrExpression,
45  QgsExpressionContext* context, bool* ok ) const
46 {
47  if ( ok )
48  *ok = false;
49 
50  if ( !mLayer )
51  return QVariant();
52 
55  if ( !context )
56  {
57  defaultContext.reset( createContext() );
58  context = defaultContext.data();
59  }
60 
61  int attrNum = mLayer->fieldNameIndex( fieldOrExpression );
62 
63  if ( attrNum == -1 )
64  {
65  Q_ASSERT( context );
66  context->setFields( mLayer->fields() );
67  // try to use expression
68  expression.reset( new QgsExpression( fieldOrExpression ) );
69 
70  if ( expression->hasParserError() || !expression->prepare( context ) )
71  {
72  return QVariant();
73  }
74  }
75 
76  QStringList lst;
77  if ( expression.isNull() )
78  lst.append( fieldOrExpression );
79  else
80  lst = expression->referencedColumns();
81 
83  .setFlags(( expression.data() && expression->needsGeometry() ) ?
86  .setSubsetOfAttributes( lst, mLayer->fields() );
87  if ( !mFilterExpression.isEmpty() )
88  request.setFilterExpression( mFilterExpression );
89  if ( context )
90  request.setExpressionContext( *context );
91 
92  //determine result type
93  QVariant::Type resultType = QVariant::Double;
94  if ( attrNum == -1 )
95  {
96  // evaluate first feature, check result type
97  QgsFeatureRequest testRequest( request );
98  testRequest.setLimit( 1 );
99  QgsFeature f;
100  QgsFeatureIterator fit = mLayer->getFeatures( testRequest );
101  if ( !fit.nextFeature( f ) )
102  {
103  //no matching features
104  if ( ok )
105  *ok = true;
106  return defaultValue( aggregate );
107  }
108 
109  if ( context )
110  context->setFeature( f );
111  QVariant v = expression->evaluate( context );
112  resultType = v.type();
113  }
114  else
115  {
116  resultType = mLayer->fields().at( attrNum ).type();
117  }
118 
119  QgsFeatureIterator fit = mLayer->getFeatures( request );
120  return calculate( aggregate, fit, resultType, attrNum, expression.data(), mDelimiter, context, ok );
121 }
122 
124 {
125  QString normalized = string.trimmed().toLower();
126 
127  if ( ok )
128  *ok = true;
129 
130  if ( normalized == "count" )
131  return Count;
132  else if ( normalized == "count_distinct" )
133  return CountDistinct;
134  else if ( normalized == "count_missing" )
135  return CountMissing;
136  else if ( normalized == "min" )
137  return Min;
138  else if ( normalized == "max" )
139  return Max;
140  else if ( normalized == "sum" )
141  return Sum;
142  else if ( normalized == "mean" )
143  return Mean;
144  else if ( normalized == "median" )
145  return Median;
146  else if ( normalized == "stdev" )
147  return StDev;
148  else if ( normalized == "stdevsample" )
149  return StDevSample;
150  else if ( normalized == "range" )
151  return Range;
152  else if ( normalized == "minority" )
153  return Minority;
154  else if ( normalized == "majority" )
155  return Majority;
156  else if ( normalized == "q1" )
157  return FirstQuartile;
158  else if ( normalized == "q3" )
159  return ThirdQuartile;
160  else if ( normalized == "iqr" )
161  return InterQuartileRange;
162  else if ( normalized == "min_length" )
163  return StringMinimumLength;
164  else if ( normalized == "max_length" )
165  return StringMaximumLength;
166  else if ( normalized == "concatenate" )
167  return StringConcatenate;
168 
169  if ( ok )
170  *ok = false;
171 
172  return Count;
173 }
174 
176  int attr, QgsExpression* expression, const QString& delimiter, QgsExpressionContext* context, bool* ok )
177 {
178  if ( ok )
179  *ok = false;
180 
181  switch ( resultType )
182  {
183  case QVariant::Int:
184  case QVariant::UInt:
185  case QVariant::LongLong:
186  case QVariant::ULongLong:
187  case QVariant::Double:
188  {
189  bool statOk = false;
190  QgsStatisticalSummary::Statistic stat = numericStatFromAggregate( aggregate, &statOk );
191  if ( !statOk )
192  return QVariant();
193 
194  if ( ok )
195  *ok = true;
196  return calculateNumericAggregate( fit, attr, expression, context, stat );
197  }
198 
199  case QVariant::Date:
200  case QVariant::DateTime:
201  {
202  bool statOk = false;
203  QgsDateTimeStatisticalSummary::Statistic stat = dateTimeStatFromAggregate( aggregate, &statOk );
204  if ( !statOk )
205  return QVariant();
206 
207  if ( ok )
208  *ok = true;
209  return calculateDateTimeAggregate( fit, attr, expression, context, stat );
210  }
211 
212  default:
213  {
214  // treat as string
215  if ( aggregate == StringConcatenate )
216  {
217  //special case
218  if ( ok )
219  *ok = true;
220  return concatenateStrings( fit, attr, expression, context, delimiter );
221  }
222 
223  bool statOk = false;
224  QgsStringStatisticalSummary::Statistic stat = stringStatFromAggregate( aggregate, &statOk );
225  if ( !statOk )
226  return QVariant();
227 
228  if ( ok )
229  *ok = true;
230  return calculateStringAggregate( fit, attr, expression, context, stat );
231  }
232  }
233 
234  return QVariant();
235 }
236 
237 QgsStatisticalSummary::Statistic QgsAggregateCalculator::numericStatFromAggregate( QgsAggregateCalculator::Aggregate aggregate, bool* ok )
238 {
239  if ( ok )
240  *ok = true;
241 
242  switch ( aggregate )
243  {
244  case Count:
246  case CountDistinct:
248  case CountMissing:
250  case Min:
252  case Max:
254  case Sum:
256  case Mean:
258  case Median:
260  case StDev:
262  case StDevSample:
264  case Range:
266  case Minority:
268  case Majority:
270  case FirstQuartile:
272  case ThirdQuartile:
274  case InterQuartileRange:
276  case StringMinimumLength:
277  case StringMaximumLength:
278  case StringConcatenate:
279  {
280  if ( ok )
281  *ok = false;
283  }
284  }
285 
286  if ( ok )
287  *ok = false;
289 }
290 
291 QgsStringStatisticalSummary::Statistic QgsAggregateCalculator::stringStatFromAggregate( QgsAggregateCalculator::Aggregate aggregate, bool* ok )
292 {
293  if ( ok )
294  *ok = true;
295 
296  switch ( aggregate )
297  {
298  case Count:
300  case CountDistinct:
302  case CountMissing:
304  case Min:
306  case Max:
308  case StringMinimumLength:
310  case StringMaximumLength:
312 
313  case Sum:
314  case Mean:
315  case Median:
316  case StDev:
317  case StDevSample:
318  case Range:
319  case Minority:
320  case Majority:
321  case FirstQuartile:
322  case ThirdQuartile:
323  case InterQuartileRange:
324  case StringConcatenate:
325  {
326  if ( ok )
327  *ok = false;
329  }
330  }
331 
332  if ( ok )
333  *ok = false;
335 }
336 
337 QgsDateTimeStatisticalSummary::Statistic QgsAggregateCalculator::dateTimeStatFromAggregate( QgsAggregateCalculator::Aggregate aggregate, bool* ok )
338 {
339  if ( ok )
340  *ok = true;
341 
342  switch ( aggregate )
343  {
344  case Count:
346  case CountDistinct:
348  case CountMissing:
350  case Min:
352  case Max:
354  case Range:
356 
357  case Sum:
358  case Mean:
359  case Median:
360  case StDev:
361  case StDevSample:
362  case Minority:
363  case Majority:
364  case FirstQuartile:
365  case ThirdQuartile:
366  case InterQuartileRange:
367  case StringMinimumLength:
368  case StringMaximumLength:
369  case StringConcatenate:
370  {
371  if ( ok )
372  *ok = false;
374  }
375  }
376 
377  if ( ok )
378  *ok = false;
380 }
381 
382 QVariant QgsAggregateCalculator::calculateNumericAggregate( QgsFeatureIterator& fit, int attr, QgsExpression* expression,
384 {
385  Q_ASSERT( expression || attr >= 0 );
386 
387  QgsStatisticalSummary s( stat );
388  QgsFeature f;
389 
390  while ( fit.nextFeature( f ) )
391  {
392  if ( expression )
393  {
394  Q_ASSERT( context );
395  context->setFeature( f );
396  QVariant v = expression->evaluate( context );
397  s.addVariant( v );
398  }
399  else
400  {
401  s.addVariant( f.attribute( attr ) );
402  }
403  }
404  s.finalize();
405  double val = s.statistic( stat );
406  return qIsNaN( val ) ? QVariant() : val;
407 }
408 
409 QVariant QgsAggregateCalculator::calculateStringAggregate( QgsFeatureIterator& fit, int attr, QgsExpression* expression,
411 {
412  Q_ASSERT( expression || attr >= 0 );
413 
414  QgsStringStatisticalSummary s( stat );
415  QgsFeature f;
416 
417  while ( fit.nextFeature( f ) )
418  {
419  if ( expression )
420  {
421  Q_ASSERT( context );
422  context->setFeature( f );
423  QVariant v = expression->evaluate( context );
424  s.addValue( v );
425  }
426  else
427  {
428  s.addValue( f.attribute( attr ) );
429  }
430  }
431  s.finalize();
432  return s.statistic( stat );
433 }
434 
435 QVariant QgsAggregateCalculator::concatenateStrings( QgsFeatureIterator& fit, int attr, QgsExpression* expression,
436  QgsExpressionContext* context, const QString& delimiter )
437 {
438  Q_ASSERT( expression || attr >= 0 );
439 
440  QgsFeature f;
441  QString result;
442  while ( fit.nextFeature( f ) )
443  {
444  if ( !result.isEmpty() )
445  result += delimiter;
446 
447  if ( expression )
448  {
449  Q_ASSERT( context );
450  context->setFeature( f );
451  QVariant v = expression->evaluate( context );
452  result += v.toString();
453  }
454  else
455  {
456  result += f.attribute( attr ).toString();
457  }
458  }
459  return result;
460 }
461 
462 QVariant QgsAggregateCalculator::defaultValue( QgsAggregateCalculator::Aggregate aggregate ) const
463 {
464  // value to return when NO features are aggregated:
465  switch ( aggregate )
466  {
467  // sensible values:
468  case Count:
469  case CountDistinct:
470  case CountMissing:
471  return 0;
472 
473  case StringConcatenate:
474  return ""; // zero length string - not null!
475 
476  // undefined - nothing makes sense here
477  case Sum:
478  case Min:
479  case Max:
480  case Mean:
481  case Median:
482  case StDev:
483  case StDevSample:
484  case Range:
485  case Minority:
486  case Majority:
487  case FirstQuartile:
488  case ThirdQuartile:
489  case InterQuartileRange:
490  case StringMinimumLength:
491  case StringMaximumLength:
492  return QVariant();
493  }
494  return QVariant();
495 }
496 
497 QVariant QgsAggregateCalculator::calculateDateTimeAggregate( QgsFeatureIterator& fit, int attr, QgsExpression* expression,
499 {
500  Q_ASSERT( expression || attr >= 0 );
501 
503  QgsFeature f;
504 
505  while ( fit.nextFeature( f ) )
506  {
507  if ( expression )
508  {
509  Q_ASSERT( context );
510  context->setFeature( f );
511  QVariant v = expression->evaluate( context );
512  s.addValue( v );
513  }
514  else
515  {
516  s.addValue( f.attribute( attr ) );
517  }
518  }
519  s.finalize();
520  return s.statistic( stat );
521 }
522 
523 QgsExpressionContext* QgsAggregateCalculator::createContext() const
524 {
529  return context;
530 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
Third quartile (numeric fields only)
Inter quartile range (IQR) (numeric fields only)
Median of values (numeric fields only)
Q_DECL_DEPRECATED QVariant evaluate(const QgsFeature *f)
Evaluate the feature and return the result.
QVariant statistic(Statistic stat) const
Returns the value of a specified statistic.
Statistic
Enumeration of flags that specify statistics to be calculated.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
First quartile (numeric fields only)
Number of missing (null) values.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
void addValue(const QVariant &value)
Adds a single datetime to the statistics calculation.
Variety (count of distinct) values.
void finalize()
Must be called after adding all values with addValues() and before retrieving any calculated statisti...
Statistic
Enumeration of flags that specify statistics to be calculated.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Interval between earliest and latest datetime value.
QgsAggregateCalculator(QgsVectorLayer *layer)
Constructor for QgsAggregateCalculator.
void addVariant(const QVariant &value)
Adds a single value to the statistics calculation.
Calculator for summary statistics and aggregates for a list of datetimes.
Sample standard deviation of values.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
Standard deviation of values (numeric fields only)
double statistic(Statistic stat) const
Returns the value of a specified statistic.
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
void setParameters(const AggregateParameters &parameters)
Sets all aggregate parameters from a parameter bundle.
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:422
void reset(T *other)
QgsFields fields() const
Returns the list of fields of this layer.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Number of missing (null) values.
void append(const T &value)
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
static Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
Minimum length of string (string fields only)
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsVectorLayer * layer() const
Returns the associated vector layer.
bool isEmpty() const
void addValue(const QVariant &value)
Adds a single variant to the statistics calculation.
QString trimmed() const
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsFeatureRequest & setFlags(const QgsFeatureRequest::Flags &flags)
Set flags that affect how features will be fetched.
Statistic
Enumeration of flags that specify statistics to be calculated.
Minimum (earliest) datetime value.
QVariant calculate(Aggregate aggregate, const QString &fieldOrExpression, QgsExpressionContext *context=nullptr, bool *ok=nullptr) const
Calculates the value of an aggregate.
Majority of values (numeric fields only)
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
T * data() const
QString toLower() const
void finalize()
Must be called after adding all datetimes with addValue() and before retrieving any calculated dateti...
QString delimiter() const
Returns the delimiter used for joining values with the StringConcatenate aggregate.
bool isNull() const
Maximum length of string (string fields only)
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
Mean of values (numeric fields only)
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Calculator for summary statistics and aggregates for a list of strings.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
Standard deviation of values.
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
void finalize()
Must be called after adding all strings with addString() and before retrieving any calculated string ...
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
Minority of values (numeric fields only)
Type type() const
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QVariant statistic(Statistic stat) const
Returns the value of a specified statistic.
Calculator for summary statistics for a list of doubles.
Represents a vector layer which manages a vector based data sets.
QVariant::Type type() const
Gets variant type of the field as it will be retrieved from data source.
Definition: qgsfield.cpp:97
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:271
Range of values (max - min) (numeric and datetime fields only)
QString toString() const
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
Sample standard deviation of values (numeric fields only)
Range of values (max - min)
Aggregate
Available aggregates to calculate.
A bundle of parameters controlling aggregate calculation.