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