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