QGIS API Documentation 3.27.0-Master (f261cc1f8b)
qgsexpressionsorter_p.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
25class 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 : std::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 indexedFeatureToAppend;
145
146 for ( const QgsFeature &f : std::as_const( features ) )
147 {
148 indexedFeatureToAppend.mIndexes.resize( mPreparedOrderBys.size() );
149 indexedFeatureToAppend.mFeature = f;
150
151 expressionContext->setFeature( indexedFeatureToAppend.mFeature );
152
153 int i = 0;
154 for ( const QgsFeatureRequest::OrderByClause &orderBy : std::as_const( mPreparedOrderBys ) )
155 {
156 indexedFeatureToAppend.mIndexes.replace( i++, orderBy.expression().evaluate( expressionContext ) );
157 }
158 indexedFeatures.append( indexedFeatureToAppend );
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 : std::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
Single scope for storing variables and functions for use within a QgsExpressionContext.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
Temporarily used structure to cache order by information.
QVector< QVariant > mIndexes
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
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2396