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