QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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"
25#include "qgsrasterlayer.h"
26#include "qgsproject.h"
27#include "qgsrelationmanager.h"
28#include "qgsvectorlayer.h"
29#include "qgsmeshlayer.h"
30#include "qgsvariantutils.h"
31
32#include <QThread>
33#include <QLocale>
34
36
37#define ENSURE_NO_EVAL_ERROR { if ( parent->hasEvalError() ) return QVariant(); }
38#define SET_EVAL_ERROR(x) { parent->setEvalErrorString( x ); return QVariant(); }
39
40#define FEAT_FROM_CONTEXT(c, f) if ( !(c) || !( c )->hasFeature() ) return QVariant(); \
41 QgsFeature f = ( c )->feature();
42
50class CORE_EXPORT QgsExpressionUtils
51{
52 public:
55// three-value logic
56 enum TVL
57 {
58 False,
59 True,
60 Unknown
61 };
62
63
64 static TVL AND[3][3];
65
66 static TVL OR[3][3];
67
68 static TVL NOT[3];
69
70#define TVL_True QVariant( 1 )
71#define TVL_False QVariant( 0 )
72#define TVL_Unknown QVariant()
73
74 static QVariant tvl2variant( TVL v )
75 {
76 switch ( v )
77 {
78 case False:
79 return TVL_False;
80 case True:
81 return TVL_True;
82 case Unknown:
83 default:
84 return TVL_Unknown;
85 }
86 }
87
88// this handles also NULL values
89 static TVL getTVLValue( const QVariant &value, QgsExpression *parent )
90 {
91 // we need to convert to TVL
92 if ( QgsVariantUtils::isNull( value ) )
93 return Unknown;
94
95 //handle some special cases
96 if ( value.userType() == QMetaType::type( "QgsGeometry" ) )
97 {
98 //geom is false if empty
99 const QgsGeometry geom = value.value<QgsGeometry>();
100 return geom.isNull() ? False : True;
101 }
102 else if ( value.userType() == QMetaType::type( "QgsFeature" ) )
103 {
104 //feat is false if non-valid
105 const QgsFeature feat = value.value<QgsFeature>();
106 return feat.isValid() ? True : False;
107 }
108
109 if ( value.type() == QVariant::Int )
110 return value.toInt() != 0 ? True : False;
111
112 bool ok;
113 const double x = value.toDouble( &ok );
114 if ( !ok )
115 {
116 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to boolean" ).arg( value.toString() ) );
117 return Unknown;
118 }
119 return !qgsDoubleNear( x, 0.0 ) ? True : False;
120 }
121
122
123 static inline bool isIntSafe( const QVariant &v )
124 {
125 if ( v.type() == QVariant::Int )
126 return true;
127 if ( v.type() == QVariant::UInt )
128 return true;
129 if ( v.type() == QVariant::LongLong )
130 return true;
131 if ( v.type() == QVariant::ULongLong )
132 return true;
133 if ( v.type() == QVariant::Double )
134 return false;
135 if ( v.type() == QVariant::String )
136 {
137 bool ok;
138 v.toString().toInt( &ok );
139 return ok;
140 }
141 return false;
142 }
143
144 static inline bool isDoubleSafe( const QVariant &v )
145 {
146 if ( v.type() == QVariant::Double )
147 return true;
148 if ( v.type() == QVariant::Int )
149 return true;
150 if ( v.type() == QVariant::UInt )
151 return true;
152 if ( v.type() == QVariant::LongLong )
153 return true;
154 if ( v.type() == QVariant::ULongLong )
155 return true;
156 if ( v.type() == QVariant::String )
157 {
158 bool ok;
159 const double val = v.toString().toDouble( &ok );
160 ok = ok && std::isfinite( val ) && !std::isnan( val );
161 return ok;
162 }
163 return false;
164 }
165
166 static inline bool isDateTimeSafe( const QVariant &v )
167 {
168 return v.type() == QVariant::DateTime
169 || v.type() == QVariant::Date
170 || v.type() == QVariant::Time;
171 }
172
173 static inline bool isIntervalSafe( const QVariant &v )
174 {
175 if ( v.userType() == QMetaType::type( "QgsInterval" ) )
176 {
177 return true;
178 }
179
180 if ( v.type() == QVariant::String )
181 {
182 return QgsInterval::fromString( v.toString() ).isValid();
183 }
184 return false;
185 }
186
187 static inline bool isNull( const QVariant &v )
188 {
189 return QgsVariantUtils::isNull( v );
190 }
191
192 static inline bool isList( const QVariant &v )
193 {
194 return v.type() == QVariant::List || v.type() == QVariant::StringList;
195 }
196
197// implicit conversion to string
198 static QString getStringValue( const QVariant &value, QgsExpression * )
199 {
200 return value.toString();
201 }
202
210 static QByteArray getBinaryValue( const QVariant &value, QgsExpression *parent )
211 {
212 if ( value.type() != QVariant::ByteArray )
213 {
214 parent->setEvalErrorString( QObject::tr( "Value is not a binary value" ) );
215 return QByteArray();
216 }
217 return value.toByteArray();
218 }
219
220 static double getDoubleValue( const QVariant &value, QgsExpression *parent )
221 {
222 bool ok;
223 const double x = value.toDouble( &ok );
224 if ( !ok || std::isnan( x ) || !std::isfinite( x ) )
225 {
226 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to double" ).arg( value.toString() ) );
227 return 0;
228 }
229 return x;
230 }
231
232 static qlonglong getIntValue( const QVariant &value, QgsExpression *parent )
233 {
234 bool ok;
235 const qlonglong x = value.toLongLong( &ok );
236 if ( ok )
237 {
238 return x;
239 }
240 else
241 {
242 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to int" ).arg( value.toString() ) );
243 return 0;
244 }
245 }
246
247 static int getNativeIntValue( const QVariant &value, QgsExpression *parent )
248 {
249 bool ok;
250 const qlonglong x = value.toLongLong( &ok );
251 if ( ok && x >= std::numeric_limits<int>::min() && x <= std::numeric_limits<int>::max() )
252 {
253 return static_cast<int>( x );
254 }
255 else
256 {
257 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to native int" ).arg( value.toString() ) );
258 return 0;
259 }
260 }
261
262 static QDateTime getDateTimeValue( const QVariant &value, QgsExpression *parent )
263 {
264 QDateTime d = value.toDateTime();
265 if ( d.isValid() )
266 {
267 return d;
268 }
269 else
270 {
271 const QTime t = value.toTime();
272 if ( t.isValid() )
273 {
274 return QDateTime( QDate( 1, 1, 1 ), t );
275 }
276
277 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( value.toString() ) );
278 return QDateTime();
279 }
280 }
281
282 static QDate getDateValue( const QVariant &value, QgsExpression *parent )
283 {
284 QDate d = value.toDate();
285 if ( d.isValid() )
286 {
287 return d;
288 }
289 else
290 {
291 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( value.toString() ) );
292 return QDate();
293 }
294 }
295
296 static QTime getTimeValue( const QVariant &value, QgsExpression *parent )
297 {
298 QTime t = value.toTime();
299 if ( t.isValid() )
300 {
301 return t;
302 }
303 else
304 {
305 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( value.toString() ) );
306 return QTime();
307 }
308 }
309
310 static QgsInterval getInterval( const QVariant &value, QgsExpression *parent, bool report_error = false )
311 {
312 if ( value.userType() == QMetaType::type( "QgsInterval" ) )
313 return value.value<QgsInterval>();
314
315 QgsInterval inter = QgsInterval::fromString( value.toString() );
316 if ( inter.isValid() )
317 {
318 return inter;
319 }
320 // If we get here then we can't convert so we just error and return invalid.
321 if ( report_error )
322 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to interval" ).arg( value.toString() ) );
323
324 return QgsInterval();
325 }
326
327 static QgsGradientColorRamp getRamp( const QVariant &value, QgsExpression *parent, bool report_error = false );
328
329 static QgsGeometry getGeometry( const QVariant &value, QgsExpression *parent )
330 {
331 if ( value.userType() == QMetaType::type( "QgsGeometry" ) )
332 return value.value<QgsGeometry>();
333
334 parent->setEvalErrorString( QStringLiteral( "Cannot convert to geometry" ) );
335 return QgsGeometry();
336 }
337
338 static QgsFeature getFeature( const QVariant &value, QgsExpression *parent )
339 {
340 if ( value.userType() == QMetaType::type( "QgsFeature" ) )
341 return value.value<QgsFeature>();
342
343 parent->setEvalErrorString( QStringLiteral( "Cannot convert to feature" ) );
344 return 0;
345 }
346
347 static QgsExpressionNode *getNode( const QVariant &value, QgsExpression *parent )
348 {
349 if ( value.canConvert<QgsExpressionNode *>() )
350 return value.value<QgsExpressionNode *>();
351
352 parent->setEvalErrorString( QStringLiteral( "Cannot convert to node" ) );
353 return nullptr;
354 }
355
356 static QgsMapLayer *getMapLayer( const QVariant &value, QgsExpression * )
357 {
358 // First check if we already received a layer pointer
359 QgsMapLayer *ml = value.value< QgsWeakMapLayerPointer >().data();
360 if ( !ml )
361 {
362 ml = value.value< QgsMapLayer * >();
363#ifdef QGISDEBUG
364 if ( ml )
365 {
366 qWarning( "Raw map layer pointer stored in expression evaluation, switch to QgsWeakMapLayerPointer instead" );
367 }
368#endif
369 }
370
371 QgsProject *project = QgsProject::instance();
372
373 // No pointer yet, maybe it's a layer id?
374 if ( !ml )
375 ml = project->mapLayer( value.toString() );
376
377 // Still nothing? Check for layer name
378 if ( !ml )
379 ml = project->mapLayersByName( value.toString() ).value( 0 );
380
381 return ml;
382 }
383
384 static std::unique_ptr<QgsVectorLayerFeatureSource> getFeatureSource( const QVariant &value, QgsExpression *e )
385 {
386 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource;
387
388 auto getFeatureSource = [ &value, e, &featureSource ]
389 {
390 QgsVectorLayer *layer = getVectorLayer( value, e );
391
392 if ( layer )
393 {
394 featureSource.reset( new QgsVectorLayerFeatureSource( layer ) );
395 }
396 };
397
398 // Make sure we only deal with the vector layer on the main thread where it lives.
399 // Anything else risks a crash.
400 if ( QThread::currentThread() == qApp->thread() )
401 getFeatureSource();
402 else
403 QMetaObject::invokeMethod( qApp, getFeatureSource, Qt::BlockingQueuedConnection );
404
405 return featureSource;
406 }
407
408 static QgsVectorLayer *getVectorLayer( const QVariant &value, QgsExpression *e )
409 {
410 return qobject_cast<QgsVectorLayer *>( getMapLayer( value, e ) );
411 }
412
413 static QgsRasterLayer *getRasterLayer( const QVariant &value, QgsExpression *e )
414 {
415 return qobject_cast<QgsRasterLayer *>( getMapLayer( value, e ) );
416 }
417
418 static QgsMeshLayer *getMeshLayer( const QVariant &value, QgsExpression *e )
419 {
420 return qobject_cast<QgsMeshLayer *>( getMapLayer( value, e ) );
421 }
422
428 static QString getFilePathValue( const QVariant &value, QgsExpression *parent );
429
430 static QVariantList getListValue( const QVariant &value, QgsExpression *parent )
431 {
432 if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
433 {
434 return value.toList();
435 }
436 else
437 {
438 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to array" ).arg( value.toString() ) );
439 return QVariantList();
440 }
441 }
442
443 static QVariantMap getMapValue( const QVariant &value, QgsExpression *parent )
444 {
445 if ( value.type() == QVariant::Map )
446 {
447 return value.toMap();
448 }
449 else
450 {
451 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to map" ).arg( value.toString() ) );
452 return QVariantMap();
453 }
454 }
455
462 static QString toLocalizedString( const QVariant &value )
463 {
464 if ( value.type() == QVariant::Int || value.type() == QVariant::UInt || value.type() == QVariant::LongLong || value.type() == QVariant::ULongLong )
465 {
466 bool ok;
467 QString res;
468
469 if ( value.type() == QVariant::ULongLong )
470 {
471 res = QLocale().toString( value.toULongLong( &ok ) );
472 }
473 else
474 {
475 res = QLocale().toString( value.toLongLong( &ok ) );
476 }
477
478 if ( ok )
479 {
480 return res;
481 }
482 else
483 {
484 return value.toString();
485 }
486 }
487 // Qt madness with QMetaType::Float :/
488 else if ( value.type() == QVariant::Double || value.type() == static_cast<QVariant::Type>( QMetaType::Float ) )
489 {
490 bool ok;
491 const QString strVal = value.toString();
492 const int dotPosition = strVal.indexOf( '.' );
493 const int precision = dotPosition > 0 ? strVal.length() - dotPosition - 1 : 0;
494 const QString res = QLocale().toString( value.toDouble( &ok ), 'f', precision );
495
496 if ( ok )
497 {
498 return res;
499 }
500 else
501 {
502 return value.toString();
503 }
504 }
505 else
506 {
507 return value.toString();
508 }
509 }
511
521 static std::tuple<QVariant::Type, int> determineResultType( const QString &expression, const QgsVectorLayer *layer, QgsFeatureRequest request = QgsFeatureRequest(), QgsExpressionContext context = QgsExpressionContext(), bool *foundFeatures = nullptr );
522
523};
524
525
526#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:56
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:219
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
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:42
bool isValid() const
Returns true if the interval is valid.
Definition: qgsinterval.h:255
static QgsInterval fromString(const QString &string)
Converts a string to an interval.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:100
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:104
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
Represents a raster layer.
static bool isNull(const QVariant &variant)
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:2527
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2147
int precision