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