QGIS API Documentation 3.99.0-Master (d270888f95f)
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 <functional>
23
25#include "qgsexpression.h"
26#include "qgsfeature.h"
27#include "qgsfeaturerequest.h"
29#include "qgsvariantutils.h"
30
31#include <QDate>
32#include <QDateTime>
33#include <QLocale>
34#include <QString>
35#include <QThread>
36#include <QTime>
37
38using namespace Qt::StringLiterals;
39
40class QgsMapLayer;
43
44#define ENSURE_NO_EVAL_ERROR { if ( parent->hasEvalError() ) return QVariant(); }
45#define SET_EVAL_ERROR(x) { parent->setEvalErrorString( x ); return QVariant(); }
46
47#define FEAT_FROM_CONTEXT(c, f) if ( !(c) || !( c )->hasFeature() ) return QVariant(); \
48 QgsFeature f = ( c )->feature();
49
56
57class CORE_EXPORT QgsExpressionUtils
58{
59 public:
62// three-value logic
63 enum TVL
64 {
65 False,
66 True,
67 Unknown
68 };
69
70
71 static TVL AND[3][3];
72
73 static TVL OR[3][3];
74
75 static TVL NOT[3];
76
77#define TVL_True QVariant( 1 )
78#define TVL_False QVariant( 0 )
79#define TVL_Unknown QVariant()
80
81 static QVariant tvl2variant( TVL v )
82 {
83 switch ( v )
84 {
85 case False:
86 return TVL_False;
87 case True:
88 return TVL_True;
89 case Unknown:
90 default:
91 return TVL_Unknown;
92 }
93 }
94
95// this handles also NULL values
96 static TVL getTVLValue( const QVariant &value, QgsExpression *parent )
97 {
98 // we need to convert to TVL
99 if ( QgsVariantUtils::isNull( value ) )
100 return Unknown;
101
102 //handle some special cases
103 int userType = value.userType();
104 if ( value.type() == QVariant::UserType )
105 {
106 if ( userType == qMetaTypeId< QgsGeometry>() || userType == qMetaTypeId<QgsReferencedGeometry>() )
107 {
108 //geom is false if empty
109 const QgsGeometry geom = getGeometry( value, nullptr );
110 return geom.isNull() ? False : True;
111 }
112 else if ( userType == qMetaTypeId<QgsFeature>() )
113 {
114 //feat is false if non-valid
115 const QgsFeature feat = value.value<QgsFeature>();
116 return feat.isValid() ? True : False;
117 }
118 }
119
120 if ( userType == QMetaType::Type::Int )
121 return value.toInt() != 0 ? True : False;
122
123 bool ok;
124 const double x = value.toDouble( &ok );
125 if ( !ok )
126 {
127 if ( parent )
128 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to boolean" ).arg( value.toString() ) );
129 return Unknown;
130 }
131 return !qgsDoubleNear( x, 0.0 ) ? True : False;
132 }
133
134
135 static inline bool isIntSafe( const QVariant &v )
136 {
137 if ( v.userType() == QMetaType::Type::Int )
138 return true;
139 if ( v.userType() == QMetaType::Type::UInt )
140 return true;
141 if ( v.userType() == QMetaType::Type::LongLong )
142 return true;
143 if ( v.userType() == QMetaType::Type::ULongLong )
144 return true;
145 if ( v.userType() == QMetaType::Type::Double )
146 return false;
147 if ( v.userType() == QMetaType::Type::QString )
148 {
149 bool ok;
150 v.toString().toInt( &ok );
151 return ok;
152 }
153 return false;
154 }
155
156 static inline bool isDoubleSafe( const QVariant &v )
157 {
158 if ( v.userType() == QMetaType::Type::Double )
159 return true;
160 if ( v.userType() == QMetaType::Type::Int )
161 return true;
162 if ( v.userType() == QMetaType::Type::UInt )
163 return true;
164 if ( v.userType() == QMetaType::Type::LongLong )
165 return true;
166 if ( v.userType() == QMetaType::Type::ULongLong )
167 return true;
168 if ( v.userType() == QMetaType::Type::QString )
169 {
170 bool ok;
171 const double val = v.toString().toDouble( &ok );
172 ok = ok && std::isfinite( val ) && !std::isnan( val );
173 return ok;
174 }
175 return false;
176 }
177
178 static inline bool isDateTimeSafe( const QVariant &v )
179 {
180 return v.userType() == QMetaType::Type::QDateTime
181 || v.userType() == QMetaType::Type::QDate
182 || v.userType() == QMetaType::Type::QTime;
183 }
184
185 static inline bool isIntervalSafe( const QVariant &v )
186 {
187 if ( v.userType() == qMetaTypeId<QgsInterval>() )
188 {
189 return true;
190 }
191
192 if ( v.userType() == QMetaType::Type::QString )
193 {
194 return QgsInterval::fromString( v.toString() ).isValid();
195 }
196 return false;
197 }
198
199 static inline bool isNull( const QVariant &v )
200 {
201 return QgsVariantUtils::isNull( v );
202 }
203
204 static inline bool isList( const QVariant &v )
205 {
206 return v.userType() == QMetaType::Type::QVariantList || v.userType() == QMetaType::Type::QStringList;
207 }
208
209 // implicit conversion to string
210 static QString getStringValue( const QVariant &value, QgsExpression * )
211 {
212 return value.toString();
213 }
214
222 static QByteArray getBinaryValue( const QVariant &value, QgsExpression *parent )
223 {
224 if ( value.userType() != QMetaType::Type::QByteArray )
225 {
226 if ( parent )
227 parent->setEvalErrorString( QObject::tr( "Value is not a binary value" ) );
228 return QByteArray();
229 }
230 return value.toByteArray();
231 }
232
233 static double getDoubleValue( const QVariant &value, QgsExpression *parent )
234 {
235 bool ok;
236 const double x = value.toDouble( &ok );
237 if ( !ok || std::isnan( x ) || !std::isfinite( x ) )
238 {
239 if ( parent )
240 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to double" ).arg( value.toString() ) );
241 return 0;
242 }
243 return x;
244 }
245
246 static qlonglong getIntValue( const QVariant &value, QgsExpression *parent )
247 {
248 bool ok;
249 const qlonglong x = value.toLongLong( &ok );
250 if ( ok )
251 {
252 return x;
253 }
254 else
255 {
256 if ( parent )
257 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to int" ).arg( value.toString() ) );
258 return 0;
259 }
260 }
261
262 static int getNativeIntValue( const QVariant &value, QgsExpression *parent )
263 {
264 bool ok;
265 const qlonglong x = value.toLongLong( &ok );
266 if ( ok && x >= std::numeric_limits<int>::min() && x <= std::numeric_limits<int>::max() )
267 {
268 return static_cast<int>( x );
269 }
270 else
271 {
272 if ( parent )
273 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to native int" ).arg( value.toString() ) );
274 return 0;
275 }
276 }
277
278 static QDateTime getDateTimeValue( const QVariant &value, QgsExpression *parent )
279 {
280 QDateTime d = value.toDateTime();
281 if ( d.isValid() )
282 {
283 return d;
284 }
285 else
286 {
287 const QTime t = value.toTime();
288 if ( t.isValid() )
289 {
290 return QDateTime( QDate( 1, 1, 1 ), t );
291 }
292
293 if ( parent )
294 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( value.toString() ) );
295 return QDateTime();
296 }
297 }
298
299 static QDate getDateValue( const QVariant &value, QgsExpression *parent )
300 {
301 QDate d = value.toDate();
302 if ( d.isValid() )
303 {
304 return d;
305 }
306 else
307 {
308 if ( parent )
309 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( value.toString() ) );
310 return QDate();
311 }
312 }
313
314 static QTime getTimeValue( const QVariant &value, QgsExpression *parent )
315 {
316 QTime t = value.toTime();
317 if ( t.isValid() )
318 {
319 return t;
320 }
321 else
322 {
323 if ( parent )
324 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( value.toString() ) );
325 return QTime();
326 }
327 }
328
329 static QColor getColorValue( const QVariant &value, QgsExpression *parent, bool &isQColor );
330
331 static QgsInterval getInterval( const QVariant &value, QgsExpression *parent, bool report_error = false )
332 {
333 if ( value.userType() == qMetaTypeId<QgsInterval>() )
334 return value.value<QgsInterval>();
335
336 QgsInterval inter = QgsInterval::fromString( value.toString() );
337 if ( inter.isValid() )
338 {
339 return inter;
340 }
341 // If we get here then we can't convert so we just error and return invalid.
342 if ( report_error && parent )
343 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to interval" ).arg( value.toString() ) );
344
345 return QgsInterval();
346 }
347
348 static QgsGradientColorRamp getRamp( const QVariant &value, QgsExpression *parent, bool report_error = false );
349
350 static QgsGeometry getGeometry( const QVariant &value, QgsExpression *parent, bool tolerant = false )
351 {
352 if ( value.userType() == qMetaTypeId< QgsReferencedGeometry>() )
353 return value.value<QgsReferencedGeometry>();
354
355 if ( value.userType() == qMetaTypeId< QgsGeometry>() )
356 return value.value<QgsGeometry>();
357
358 if ( !tolerant && parent )
359 parent->setEvalErrorString( u"Cannot convert to geometry"_s );
360 return QgsGeometry();
361 }
362
363 static QgsFeature getFeature( const QVariant &value, QgsExpression *parent )
364 {
365 if ( value.userType() == qMetaTypeId<QgsFeature>() )
366 return value.value<QgsFeature>();
367
368 if ( parent )
369 parent->setEvalErrorString( u"Cannot convert to feature"_s );
370 return 0;
371 }
372
378 static QgsCoordinateReferenceSystem getCrsValue( const QVariant &value, QgsExpression *parent );
379
385 static QTimeZone getTimeZoneValue( const QVariant &value, QgsExpression *parent );
386
387 static QgsExpressionNode *getNode( const QVariant &value, QgsExpression *parent )
388 {
389 if ( value.canConvert<QgsExpressionNode *>() )
390 return value.value<QgsExpressionNode *>();
391
392 if ( parent )
393 parent->setEvalErrorString( u"Cannot convert to node"_s );
394 return nullptr;
395 }
396
400 Q_DECL_DEPRECATED static QgsMapLayer *getMapLayer( const QVariant &value, const QgsExpressionContext *context, QgsExpression * );
401
407 static void executeLambdaForMapLayer( const QVariant &value, const QgsExpressionContext *context, QgsExpression *expression, const std::function< void( QgsMapLayer * )> &function, bool &foundLayer );
408
414 static QVariant runMapLayerFunctionThreadSafe( const QVariant &value, const QgsExpressionContext *context, QgsExpression *expression, const std::function<QVariant( QgsMapLayer * ) > &function, bool &foundLayer );
415
419 static std::unique_ptr<QgsVectorLayerFeatureSource> getFeatureSource( const QVariant &value, const QgsExpressionContext *context, QgsExpression *e, bool &foundLayer );
420
424 Q_DECL_DEPRECATED static QgsVectorLayer *getVectorLayer( const QVariant &value, const QgsExpressionContext *context, QgsExpression *e );
425
431 static QString getFilePathValue( const QVariant &value, const QgsExpressionContext *context, QgsExpression *parent );
432
433 static QVariantList getListValue( const QVariant &value, QgsExpression *parent )
434 {
435 if ( value.userType() == QMetaType::Type::QVariantList || value.userType() == QMetaType::Type::QStringList )
436 {
437 return value.toList();
438 }
439 else
440 {
441 if ( parent )
442 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to array" ).arg( value.toString() ) );
443 return QVariantList();
444 }
445 }
446
447 static QVariantMap getMapValue( const QVariant &value, QgsExpression *parent )
448 {
449 if ( value.userType() == QMetaType::Type::QVariantMap )
450 {
451 return value.toMap();
452 }
453 else
454 {
455 if ( parent )
456 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to map" ).arg( value.toString() ) );
457 return QVariantMap();
458 }
459 }
460
467 static QString toLocalizedString( const QVariant &value )
468 {
469 if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::UInt || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::ULongLong )
470 {
471 bool ok;
472 QString res;
473
474 if ( value.userType() == QMetaType::Type::ULongLong )
475 {
476 res = QLocale().toString( value.toULongLong( &ok ) );
477 }
478 else
479 {
480 res = QLocale().toString( value.toLongLong( &ok ) );
481 }
482
483 if ( ok )
484 {
485 return res;
486 }
487 else
488 {
489 return value.toString();
490 }
491 }
492 // Qt madness with QMetaType::Float :/
493 else if ( value.userType() == QMetaType::Type::Double || value.userType() == static_cast<QMetaType::Type>( QMetaType::Float ) )
494 {
495 bool ok;
496 const QString strVal = value.toString();
497 const int dotPosition = strVal.indexOf( '.' );
498 const int precision = dotPosition > 0 ? strVal.length() - dotPosition - 1 : 0;
499 const QString res = QLocale().toString( value.toDouble( &ok ), 'f', precision );
500
501 if ( ok )
502 {
503 return res;
504 }
505 else
506 {
507 return value.toString();
508 }
509 }
510 else
511 {
512 return value.toString();
513 }
514 }
516
526 static std::tuple<QMetaType::Type, int> determineResultType( const QString &expression, const QgsVectorLayer *layer, const QgsFeatureRequest &request = QgsFeatureRequest(), const QgsExpressionContext &context = QgsExpressionContext(), bool *foundFeatures = nullptr );
527
528 private:
529
533 static QgsMapLayer *getMapLayerPrivate( const QVariant &value, const QgsExpressionContext *context, QgsExpression * );
534
535};
536
537
538#endif // QGSEXPRESSIONUTILS_H
Represents a coordinate reference system (CRS).
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.
static std::tuple< QMetaType::Type, int > determineResultType(const QString &expression, const QgsVectorLayer *layer, const QgsFeatureRequest &request=QgsFeatureRequest(), const QgsExpressionContext &context=QgsExpressionContext(), bool *foundFeatures=nullptr)
Returns a value type and user type for a given expression.
Handles parsing and evaluation of expressions (formerly called "search strings").
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions).
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:60
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:50
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:83
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 dataset.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900