QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
qgsexpressionutils.h
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionutils.h
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
17#ifndef QGSEXPRESSIONUTILS_H
18#define QGSEXPRESSIONUTILS_H
19
20#define SIP_NO_FILE
21
22#include "qgsfeature.h"
23#include "qgsexpression.h"
24#include "qgsvariantutils.h"
25#include "qgsfeaturerequest.h"
27
28#include <QDate>
29#include <QDateTime>
30#include <QTime>
31#include <QThread>
32#include <QLocale>
33#include <functional>
34
35class QgsMapLayer;
38
39#define ENSURE_NO_EVAL_ERROR { if ( parent->hasEvalError() ) return QVariant(); }
40#define SET_EVAL_ERROR(x) { parent->setEvalErrorString( x ); return QVariant(); }
41
42#define FEAT_FROM_CONTEXT(c, f) if ( !(c) || !( c )->hasFeature() ) return QVariant(); \
43 QgsFeature f = ( c )->feature();
44
52class CORE_EXPORT QgsExpressionUtils
53{
54 public:
57// three-value logic
58 enum TVL
59 {
60 False,
61 True,
62 Unknown
63 };
64
65
66 static TVL AND[3][3];
67
68 static TVL OR[3][3];
69
70 static TVL NOT[3];
71
72#define TVL_True QVariant( 1 )
73#define TVL_False QVariant( 0 )
74#define TVL_Unknown QVariant()
75
76 static QVariant tvl2variant( TVL v )
77 {
78 switch ( v )
79 {
80 case False:
81 return TVL_False;
82 case True:
83 return TVL_True;
84 case Unknown:
85 default:
86 return TVL_Unknown;
87 }
88 }
89
90// this handles also NULL values
91 static TVL getTVLValue( const QVariant &value, QgsExpression *parent )
92 {
93 // we need to convert to TVL
94 if ( QgsVariantUtils::isNull( value ) )
95 return Unknown;
96
97 //handle some special cases
98 int userType = value.userType();
99 if ( value.type() == QVariant::UserType )
100 {
101 if ( userType == qMetaTypeId< QgsGeometry>() || userType == qMetaTypeId<QgsReferencedGeometry>() )
102 {
103 //geom is false if empty
104 const QgsGeometry geom = getGeometry( value, nullptr );
105 return geom.isNull() ? False : True;
106 }
107 else if ( userType == qMetaTypeId<QgsFeature>() )
108 {
109 //feat is false if non-valid
110 const QgsFeature feat = value.value<QgsFeature>();
111 return feat.isValid() ? True : False;
112 }
113 }
114
115 if ( userType == QMetaType::Type::Int )
116 return value.toInt() != 0 ? True : False;
117
118 bool ok;
119 const double x = value.toDouble( &ok );
120 if ( !ok )
121 {
122 if ( parent )
123 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to boolean" ).arg( value.toString() ) );
124 return Unknown;
125 }
126 return !qgsDoubleNear( x, 0.0 ) ? True : False;
127 }
128
129
130 static inline bool isIntSafe( const QVariant &v )
131 {
132 if ( v.userType() == QMetaType::Type::Int )
133 return true;
134 if ( v.userType() == QMetaType::Type::UInt )
135 return true;
136 if ( v.userType() == QMetaType::Type::LongLong )
137 return true;
138 if ( v.userType() == QMetaType::Type::ULongLong )
139 return true;
140 if ( v.userType() == QMetaType::Type::Double )
141 return false;
142 if ( v.userType() == QMetaType::Type::QString )
143 {
144 bool ok;
145 v.toString().toInt( &ok );
146 return ok;
147 }
148 return false;
149 }
150
151 static inline bool isDoubleSafe( const QVariant &v )
152 {
153 if ( v.userType() == QMetaType::Type::Double )
154 return true;
155 if ( v.userType() == QMetaType::Type::Int )
156 return true;
157 if ( v.userType() == QMetaType::Type::UInt )
158 return true;
159 if ( v.userType() == QMetaType::Type::LongLong )
160 return true;
161 if ( v.userType() == QMetaType::Type::ULongLong )
162 return true;
163 if ( v.userType() == QMetaType::Type::QString )
164 {
165 bool ok;
166 const double val = v.toString().toDouble( &ok );
167 ok = ok && std::isfinite( val ) && !std::isnan( val );
168 return ok;
169 }
170 return false;
171 }
172
173 static inline bool isDateTimeSafe( const QVariant &v )
174 {
175 return v.userType() == QMetaType::Type::QDateTime
176 || v.userType() == QMetaType::Type::QDate
177 || v.userType() == QMetaType::Type::QTime;
178 }
179
180 static inline bool isIntervalSafe( const QVariant &v )
181 {
182 if ( v.userType() == qMetaTypeId<QgsInterval>() )
183 {
184 return true;
185 }
186
187 if ( v.userType() == QMetaType::Type::QString )
188 {
189 return QgsInterval::fromString( v.toString() ).isValid();
190 }
191 return false;
192 }
193
194 static inline bool isNull( const QVariant &v )
195 {
196 return QgsVariantUtils::isNull( v );
197 }
198
199 static inline bool isList( const QVariant &v )
200 {
201 return v.userType() == QMetaType::Type::QVariantList || v.userType() == QMetaType::Type::QStringList;
202 }
203
204// implicit conversion to string
205 static QString getStringValue( const QVariant &value, QgsExpression * )
206 {
207 return value.toString();
208 }
209
217 static QByteArray getBinaryValue( const QVariant &value, QgsExpression *parent )
218 {
219 if ( value.userType() != QMetaType::Type::QByteArray )
220 {
221 if ( parent )
222 parent->setEvalErrorString( QObject::tr( "Value is not a binary value" ) );
223 return QByteArray();
224 }
225 return value.toByteArray();
226 }
227
228 static double getDoubleValue( const QVariant &value, QgsExpression *parent )
229 {
230 bool ok;
231 const double x = value.toDouble( &ok );
232 if ( !ok || std::isnan( x ) || !std::isfinite( x ) )
233 {
234 if ( parent )
235 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to double" ).arg( value.toString() ) );
236 return 0;
237 }
238 return x;
239 }
240
241 static qlonglong getIntValue( const QVariant &value, QgsExpression *parent )
242 {
243 bool ok;
244 const qlonglong x = value.toLongLong( &ok );
245 if ( ok )
246 {
247 return x;
248 }
249 else
250 {
251 if ( parent )
252 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to int" ).arg( value.toString() ) );
253 return 0;
254 }
255 }
256
257 static int getNativeIntValue( const QVariant &value, QgsExpression *parent )
258 {
259 bool ok;
260 const qlonglong x = value.toLongLong( &ok );
261 if ( ok && x >= std::numeric_limits<int>::min() && x <= std::numeric_limits<int>::max() )
262 {
263 return static_cast<int>( x );
264 }
265 else
266 {
267 if ( parent )
268 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to native int" ).arg( value.toString() ) );
269 return 0;
270 }
271 }
272
273 static QDateTime getDateTimeValue( const QVariant &value, QgsExpression *parent )
274 {
275 QDateTime d = value.toDateTime();
276 if ( d.isValid() )
277 {
278 return d;
279 }
280 else
281 {
282 const QTime t = value.toTime();
283 if ( t.isValid() )
284 {
285 return QDateTime( QDate( 1, 1, 1 ), t );
286 }
287
288 if ( parent )
289 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( value.toString() ) );
290 return QDateTime();
291 }
292 }
293
294 static QDate getDateValue( const QVariant &value, QgsExpression *parent )
295 {
296 QDate d = value.toDate();
297 if ( d.isValid() )
298 {
299 return d;
300 }
301 else
302 {
303 if ( parent )
304 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( value.toString() ) );
305 return QDate();
306 }
307 }
308
309 static QTime getTimeValue( const QVariant &value, QgsExpression *parent )
310 {
311 QTime t = value.toTime();
312 if ( t.isValid() )
313 {
314 return t;
315 }
316 else
317 {
318 if ( parent )
319 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( value.toString() ) );
320 return QTime();
321 }
322 }
323
324 static QColor getColorValue( const QVariant &value, QgsExpression *parent, bool &isQColor );
325
326 static QgsInterval getInterval( const QVariant &value, QgsExpression *parent, bool report_error = false )
327 {
328 if ( value.userType() == qMetaTypeId<QgsInterval>() )
329 return value.value<QgsInterval>();
330
331 QgsInterval inter = QgsInterval::fromString( value.toString() );
332 if ( inter.isValid() )
333 {
334 return inter;
335 }
336 // If we get here then we can't convert so we just error and return invalid.
337 if ( report_error && parent )
338 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to interval" ).arg( value.toString() ) );
339
340 return QgsInterval();
341 }
342
343 static QgsGradientColorRamp getRamp( const QVariant &value, QgsExpression *parent, bool report_error = false );
344
345 static QgsGeometry getGeometry( const QVariant &value, QgsExpression *parent, bool tolerant = false )
346 {
347 if ( value.userType() == qMetaTypeId< QgsReferencedGeometry>() )
348 return value.value<QgsReferencedGeometry>();
349
350 if ( value.userType() == qMetaTypeId< QgsGeometry>() )
351 return value.value<QgsGeometry>();
352
353 if ( !tolerant && parent )
354 parent->setEvalErrorString( QStringLiteral( "Cannot convert to geometry" ) );
355 return QgsGeometry();
356 }
357
358 static QgsFeature getFeature( const QVariant &value, QgsExpression *parent )
359 {
360 if ( value.userType() == qMetaTypeId<QgsFeature>() )
361 return value.value<QgsFeature>();
362
363 if ( parent )
364 parent->setEvalErrorString( QStringLiteral( "Cannot convert to feature" ) );
365 return 0;
366 }
367
368 static QgsExpressionNode *getNode( const QVariant &value, QgsExpression *parent )
369 {
370 if ( value.canConvert<QgsExpressionNode *>() )
371 return value.value<QgsExpressionNode *>();
372
373 if ( parent )
374 parent->setEvalErrorString( QStringLiteral( "Cannot convert to node" ) );
375 return nullptr;
376 }
377
381 Q_DECL_DEPRECATED static QgsMapLayer *getMapLayer( const QVariant &value, const QgsExpressionContext *context, QgsExpression * );
382
388 static void executeLambdaForMapLayer( const QVariant &value, const QgsExpressionContext *context, QgsExpression *expression, const std::function< void( QgsMapLayer * )> &function, bool &foundLayer );
389
395 static QVariant runMapLayerFunctionThreadSafe( const QVariant &value, const QgsExpressionContext *context, QgsExpression *expression, const std::function<QVariant( QgsMapLayer * ) > &function, bool &foundLayer );
396
400 static std::unique_ptr<QgsVectorLayerFeatureSource> getFeatureSource( const QVariant &value, const QgsExpressionContext *context, QgsExpression *e, bool &foundLayer );
401
405 Q_DECL_DEPRECATED static QgsVectorLayer *getVectorLayer( const QVariant &value, const QgsExpressionContext *context, QgsExpression *e );
406
412 static QString getFilePathValue( const QVariant &value, const QgsExpressionContext *context, QgsExpression *parent );
413
414 static QVariantList getListValue( const QVariant &value, QgsExpression *parent )
415 {
416 if ( value.userType() == QMetaType::Type::QVariantList || value.userType() == QMetaType::Type::QStringList )
417 {
418 return value.toList();
419 }
420 else
421 {
422 if ( parent )
423 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to array" ).arg( value.toString() ) );
424 return QVariantList();
425 }
426 }
427
428 static QVariantMap getMapValue( const QVariant &value, QgsExpression *parent )
429 {
430 if ( value.userType() == QMetaType::Type::QVariantMap )
431 {
432 return value.toMap();
433 }
434 else
435 {
436 if ( parent )
437 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to map" ).arg( value.toString() ) );
438 return QVariantMap();
439 }
440 }
441
448 static QString toLocalizedString( const QVariant &value )
449 {
450 if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::UInt || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::ULongLong )
451 {
452 bool ok;
453 QString res;
454
455 if ( value.userType() == QMetaType::Type::ULongLong )
456 {
457 res = QLocale().toString( value.toULongLong( &ok ) );
458 }
459 else
460 {
461 res = QLocale().toString( value.toLongLong( &ok ) );
462 }
463
464 if ( ok )
465 {
466 return res;
467 }
468 else
469 {
470 return value.toString();
471 }
472 }
473 // Qt madness with QMetaType::Float :/
474 else if ( value.userType() == QMetaType::Type::Double || value.userType() == static_cast<QMetaType::Type>( QMetaType::Float ) )
475 {
476 bool ok;
477 const QString strVal = value.toString();
478 const int dotPosition = strVal.indexOf( '.' );
479 const int precision = dotPosition > 0 ? strVal.length() - dotPosition - 1 : 0;
480 const QString res = QLocale().toString( value.toDouble( &ok ), 'f', precision );
481
482 if ( ok )
483 {
484 return res;
485 }
486 else
487 {
488 return value.toString();
489 }
490 }
491 else
492 {
493 return value.toString();
494 }
495 }
497
507 static std::tuple<QMetaType::Type, int> determineResultType( const QString &expression, const QgsVectorLayer *layer, QgsFeatureRequest request = QgsFeatureRequest(), QgsExpressionContext context = QgsExpressionContext(), bool *foundFeatures = nullptr );
508
509 private:
510
514 static QgsMapLayer *getMapLayerPrivate( const QVariant &value, const QgsExpressionContext *context, QgsExpression * );
515
516};
517
518
519#endif // QGSEXPRESSIONUTILS_H
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Abstract base class for all nodes that can appear in an expression.
A set of expression-related functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
This class wraps a request for features to a vector layer (or directly its vector data provider).
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
bool isValid() const
Returns the validity of this feature.
A geometry is the spatial representation of a feature.
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
A representation of the interval between two datetime values.
Definition qgsinterval.h:46
bool isValid() const
Returns true if the interval is valid.
static QgsInterval fromString(const QString &string)
Converts a string to an interval.
Base class for all map layer types.
Definition qgsmaplayer.h:76
A QgsGeometry with associated coordinate reference system.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Partial snapshot of vector layer's state (only the members necessary for access to features)
Represents a vector layer which manages a vector based data sets.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5958
int precision