QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsexpressionsorter.h
Go to the documentation of this file.
1 /***************************************************************************
2  qgsexpressionsorter.h - QgsExpressionSorter
3  -------------------------------------------
4 
5  begin : 15.1.2016
6  Copyright : (C) 2016 Matthias Kuhn
7  Email : matthias at opengis dot ch
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 #ifndef QGSEXPRESSIONSORTER_H
17 #define QGSEXPRESSIONSORTER_H
18 
19 #include <QLocale>
20 
21 #include "qgsfeaturerequest.h"
22 #include "qgsindexedfeature.h"
23 
25 class QgsExpressionSorter
26 {
27  public:
28  explicit QgsExpressionSorter( const QList<QgsFeatureRequest::OrderByClause> &preparedOrderBys )
29  : mPreparedOrderBys( preparedOrderBys )
30  // QString::localeAwareCompare() is case insensitive for common locales,
31  // but case sensitive for the C locale. So use an explicit case
32  // insensitive comparison in that later case to avoid test failures.
33  , mUseCaseInsensitiveComparison( QLocale().name() == QLocale::c().name() )
34  {}
35 
36  bool operator()( const QgsIndexedFeature &f1, const QgsIndexedFeature &f2 ) const
37  {
38  int i = 0;
39  for ( const QgsFeatureRequest::OrderByClause &orderBy : qgis::as_const( mPreparedOrderBys ) )
40  {
41  const QVariant &v1 = f1.mIndexes.at( i );
42  const QVariant &v2 = f2.mIndexes.at( i );
43  ++i;
44 
45  // Both NULL: don't care
46  if ( v1.isNull() && v2.isNull() )
47  continue;
48 
49  // Check for NULLs first
50  if ( v1.isNull() != v2.isNull() )
51  {
52  if ( orderBy.nullsFirst() )
53  return v1.isNull();
54  else
55  return !v1.isNull();
56  }
57 
58  // Both values are not NULL
59  switch ( v1.type() )
60  {
61  case QVariant::Int:
62  case QVariant::UInt:
63  case QVariant::LongLong:
64  case QVariant::ULongLong:
65  if ( v1.toLongLong() == v2.toLongLong() )
66  continue;
67  if ( orderBy.ascending() )
68  return v1.toLongLong() < v2.toLongLong();
69  else
70  return v1.toLongLong() > v2.toLongLong();
71 
72  case QVariant::Double:
73  if ( qgsDoubleNear( v1.toDouble(), v2.toDouble() ) )
74  continue;
75  if ( orderBy.ascending() )
76  return v1.toDouble() < v2.toDouble();
77  else
78  return v1.toDouble() > v2.toDouble();
79 
80  case QVariant::Date:
81  if ( v1.toDate() == v2.toDate() )
82  continue;
83  if ( orderBy.ascending() )
84  return v1.toDate() < v2.toDate();
85  else
86  return v1.toDate() > v2.toDate();
87 
88  case QVariant::Time:
89  if ( v1.toTime() == v2.toTime() )
90  continue;
91  if ( orderBy.ascending() )
92  return v1.toTime() < v2.toTime();
93  else
94  return v1.toTime() > v2.toTime();
95 
96  case QVariant::DateTime:
97  if ( v1.toDateTime() == v2.toDateTime() )
98  continue;
99  if ( orderBy.ascending() )
100  return v1.toDateTime() < v2.toDateTime();
101  else
102  return v1.toDateTime() > v2.toDateTime();
103 
104  case QVariant::Bool:
105  if ( v1.toBool() == v2.toBool() )
106  continue;
107  if ( orderBy.ascending() )
108  return !v1.toBool();
109  else
110  return v1.toBool();
111 
112  default:
113  if ( 0 == v1.toString().localeAwareCompare( v2.toString() ) )
114  continue;
115  if ( mUseCaseInsensitiveComparison )
116  {
117  if ( orderBy.ascending() )
118  return v1.toString().compare( v2.toString(), Qt::CaseInsensitive ) < 0;
119  else
120  return v1.toString().compare( v2.toString(), Qt::CaseInsensitive ) > 0;
121  }
122  else
123  {
124  if ( orderBy.ascending() )
125  return v1.toString().localeAwareCompare( v2.toString() ) < 0;
126  else
127  return v1.toString().localeAwareCompare( v2.toString() ) > 0;
128  }
129  }
130  }
131 
132  // Equal
133  return false;
134  }
135 
136  void sortFeatures( QList<QgsFeature> &features, QgsExpressionContext *expressionContext )
137  {
138  QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Expression Sorter" ) );
139 
140  expressionContext->appendScope( scope );
141 
142  QVector<QgsIndexedFeature> indexedFeatures;
143 
144  QgsIndexedFeature indexedFeature;
145 
146  for ( const QgsFeature &f : qgis::as_const( features ) )
147  {
148  indexedFeature.mIndexes.resize( mPreparedOrderBys.size() );
149  indexedFeature.mFeature = f;
150 
151  expressionContext->setFeature( indexedFeature.mFeature );
152 
153  int i = 0;
154  for ( const QgsFeatureRequest::OrderByClause &orderBy : qgis::as_const( mPreparedOrderBys ) )
155  {
156  indexedFeature.mIndexes.replace( i++, orderBy.expression().evaluate( expressionContext ) );
157  }
158  indexedFeatures.append( indexedFeature );
159  }
160 
161  delete expressionContext->popScope();
162 
163  std::sort( indexedFeatures.begin(), indexedFeatures.end(), *this );
164 
165  features.clear();
166 
167  for ( const QgsIndexedFeature &indexedFeature : qgis::as_const( indexedFeatures ) )
168  features.append( indexedFeature.mFeature );
169  }
170 
171  private:
172  QList<QgsFeatureRequest::OrderByClause> mPreparedOrderBys;
173  bool mUseCaseInsensitiveComparison;
174 };
175 
177 
178 
179 #endif // QGSEXPRESSIONSORTER_H
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:280
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Temporarily used structure to cache order by information.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Single scope for storing variables and functions for use within a QgsExpressionContext.
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QVector< QVariant > mIndexes
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.