QGIS API Documentation  3.19.0-Master (c022ae99b5)
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 
31 #include <QThread>
32 
33 #define ENSURE_NO_EVAL_ERROR { if ( parent->hasEvalError() ) return QVariant(); }
34 #define SET_EVAL_ERROR(x) { parent->setEvalErrorString( x ); return QVariant(); }
35 
36 #define FEAT_FROM_CONTEXT(c, f) if ( !(c) || !( c )->hasFeature() ) return QVariant(); \
37  QgsFeature f = ( c )->feature();
38 
40 // three-value logic
41 
43 class QgsExpressionUtils
44 {
45  public:
46  enum TVL
47  {
48  False,
49  True,
50  Unknown
51  };
52 
53 
54  static TVL AND[3][3];
55 
56  static TVL OR[3][3];
57 
58  static TVL NOT[3];
59 
60 #define TVL_True QVariant( 1 )
61 #define TVL_False QVariant( 0 )
62 #define TVL_Unknown QVariant()
63 
64  static QVariant tvl2variant( TVL v )
65  {
66  switch ( v )
67  {
68  case False:
69  return TVL_False;
70  case True:
71  return TVL_True;
72  case Unknown:
73  default:
74  return TVL_Unknown;
75  }
76  }
77 
78 // this handles also NULL values
79  static TVL getTVLValue( const QVariant &value, QgsExpression *parent )
80  {
81  // we need to convert to TVL
82  if ( value.isNull() )
83  return Unknown;
84 
85  //handle some special cases
86  if ( value.canConvert<QgsGeometry>() )
87  {
88  //geom is false if empty
89  QgsGeometry geom = value.value<QgsGeometry>();
90  return geom.isNull() ? False : True;
91  }
92  else if ( value.canConvert<QgsFeature>() )
93  {
94  //feat is false if non-valid
95  QgsFeature feat = value.value<QgsFeature>();
96  return feat.isValid() ? True : False;
97  }
98 
99  if ( value.type() == QVariant::Int )
100  return value.toInt() != 0 ? True : False;
101 
102  bool ok;
103  double x = value.toDouble( &ok );
104  if ( !ok )
105  {
106  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to boolean" ).arg( value.toString() ) );
107  return Unknown;
108  }
109  return !qgsDoubleNear( x, 0.0 ) ? True : False;
110  }
111 
112 
113  static inline bool isIntSafe( const QVariant &v )
114  {
115  if ( v.type() == QVariant::Int )
116  return true;
117  if ( v.type() == QVariant::UInt )
118  return true;
119  if ( v.type() == QVariant::LongLong )
120  return true;
121  if ( v.type() == QVariant::ULongLong )
122  return true;
123  if ( v.type() == QVariant::Double )
124  return false;
125  if ( v.type() == QVariant::String )
126  {
127  bool ok;
128  v.toString().toInt( &ok );
129  return ok;
130  }
131  return false;
132  }
133 
134  static inline bool isDoubleSafe( const QVariant &v )
135  {
136  if ( v.type() == QVariant::Double )
137  return true;
138  if ( v.type() == QVariant::Int )
139  return true;
140  if ( v.type() == QVariant::UInt )
141  return true;
142  if ( v.type() == QVariant::LongLong )
143  return true;
144  if ( v.type() == QVariant::ULongLong )
145  return true;
146  if ( v.type() == QVariant::String )
147  {
148  bool ok;
149  double val = v.toString().toDouble( &ok );
150  ok = ok && std::isfinite( val ) && !std::isnan( val );
151  return ok;
152  }
153  return false;
154  }
155 
156  static inline bool isDateTimeSafe( const QVariant &v )
157  {
158  return v.type() == QVariant::DateTime
159  || v.type() == QVariant::Date
160  || v.type() == QVariant::Time;
161  }
162 
163  static inline bool isIntervalSafe( const QVariant &v )
164  {
165  if ( v.canConvert<QgsInterval>() )
166  {
167  return true;
168  }
169 
170  if ( v.type() == QVariant::String )
171  {
172  return QgsInterval::fromString( v.toString() ).isValid();
173  }
174  return false;
175  }
176 
177  static inline bool isNull( const QVariant &v )
178  {
179  return v.isNull();
180  }
181 
182  static inline bool isList( const QVariant &v )
183  {
184  return v.type() == QVariant::List;
185  }
186 
187 // implicit conversion to string
188  static QString getStringValue( const QVariant &value, QgsExpression * )
189  {
190  return value.toString();
191  }
192 
200  static QByteArray getBinaryValue( const QVariant &value, QgsExpression *parent )
201  {
202  if ( value.type() != QVariant::ByteArray )
203  {
204  parent->setEvalErrorString( QObject::tr( "Value is not a binary value" ) );
205  return QByteArray();
206  }
207  return value.toByteArray();
208  }
209 
210  static double getDoubleValue( const QVariant &value, QgsExpression *parent )
211  {
212  bool ok;
213  double x = value.toDouble( &ok );
214  if ( !ok || std::isnan( x ) || !std::isfinite( x ) )
215  {
216  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to double" ).arg( value.toString() ) );
217  return 0;
218  }
219  return x;
220  }
221 
222  static qlonglong getIntValue( const QVariant &value, QgsExpression *parent )
223  {
224  bool ok;
225  qlonglong x = value.toLongLong( &ok );
226  if ( ok )
227  {
228  return x;
229  }
230  else
231  {
232  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to int" ).arg( value.toString() ) );
233  return 0;
234  }
235  }
236 
237  static int getNativeIntValue( const QVariant &value, QgsExpression *parent )
238  {
239  bool ok;
240  qlonglong x = value.toLongLong( &ok );
241  if ( ok && x >= std::numeric_limits<int>::min() && x <= std::numeric_limits<int>::max() )
242  {
243  return static_cast<int>( x );
244  }
245  else
246  {
247  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to native int" ).arg( value.toString() ) );
248  return 0;
249  }
250  }
251 
252  static QDateTime getDateTimeValue( const QVariant &value, QgsExpression *parent )
253  {
254  QDateTime d = value.toDateTime();
255  if ( d.isValid() )
256  {
257  return d;
258  }
259  else
260  {
261  QTime t = value.toTime();
262  if ( t.isValid() )
263  {
264  return QDateTime( QDate( 1, 1, 1 ), t );
265  }
266 
267  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( value.toString() ) );
268  return QDateTime();
269  }
270  }
271 
272  static QDate getDateValue( const QVariant &value, QgsExpression *parent )
273  {
274  QDate d = value.toDate();
275  if ( d.isValid() )
276  {
277  return d;
278  }
279  else
280  {
281  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( value.toString() ) );
282  return QDate();
283  }
284  }
285 
286  static QTime getTimeValue( const QVariant &value, QgsExpression *parent )
287  {
288  QTime t = value.toTime();
289  if ( t.isValid() )
290  {
291  return t;
292  }
293  else
294  {
295  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( value.toString() ) );
296  return QTime();
297  }
298  }
299 
300  static QgsInterval getInterval( const QVariant &value, QgsExpression *parent, bool report_error = false )
301  {
302  if ( value.canConvert<QgsInterval>() )
303  return value.value<QgsInterval>();
304 
305  QgsInterval inter = QgsInterval::fromString( value.toString() );
306  if ( inter.isValid() )
307  {
308  return inter;
309  }
310  // If we get here then we can't convert so we just error and return invalid.
311  if ( report_error )
312  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to interval" ).arg( value.toString() ) );
313 
314  return QgsInterval();
315  }
316 
317  static QgsGradientColorRamp getRamp( const QVariant &value, QgsExpression *parent, bool report_error = false )
318  {
319  if ( value.canConvert<QgsGradientColorRamp>() )
320  return value.value<QgsGradientColorRamp>();
321 
322  // If we get here then we can't convert so we just error and return invalid.
323  if ( report_error )
324  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to gradient ramp" ).arg( value.toString() ) );
325 
326  return QgsGradientColorRamp();
327  }
328 
329  static QgsGeometry getGeometry( const QVariant &value, QgsExpression *parent )
330  {
331  if ( value.canConvert<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.canConvert<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  QgsProject *project = QgsProject::instance();
361 
362  // No pointer yet, maybe it's a layer id?
363  if ( !ml )
364  ml = project->mapLayer( value.toString() );
365 
366  // Still nothing? Check for layer name
367  if ( !ml )
368  ml = project->mapLayersByName( value.toString() ).value( 0 );
369 
370  return ml;
371  }
372 
373  static std::unique_ptr<QgsVectorLayerFeatureSource> getFeatureSource( const QVariant &value, QgsExpression *e )
374  {
375  std::unique_ptr<QgsVectorLayerFeatureSource> featureSource;
376 
377  auto getFeatureSource = [ &value, e, &featureSource ]
378  {
379  QgsVectorLayer *layer = getVectorLayer( value, e );
380 
381  if ( layer )
382  {
383  featureSource.reset( new QgsVectorLayerFeatureSource( layer ) );
384  }
385  };
386 
387  // Make sure we only deal with the vector layer on the main thread where it lives.
388  // Anything else risks a crash.
389  if ( QThread::currentThread() == qApp->thread() )
390  getFeatureSource();
391  else
392  QMetaObject::invokeMethod( qApp, getFeatureSource, Qt::BlockingQueuedConnection );
393 
394  return featureSource;
395  }
396 
397  static QgsVectorLayer *getVectorLayer( const QVariant &value, QgsExpression *e )
398  {
399  return qobject_cast<QgsVectorLayer *>( getMapLayer( value, e ) );
400  }
401 
402  static QgsRasterLayer *getRasterLayer( const QVariant &value, QgsExpression *e )
403  {
404  return qobject_cast<QgsRasterLayer *>( getMapLayer( value, e ) );
405  }
406 
407  static QVariantList getListValue( const QVariant &value, QgsExpression *parent )
408  {
409  if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
410  {
411  return value.toList();
412  }
413  else
414  {
415  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to array" ).arg( value.toString() ) );
416  return QVariantList();
417  }
418  }
419 
420  static QVariantMap getMapValue( const QVariant &value, QgsExpression *parent )
421  {
422  if ( value.type() == QVariant::Map )
423  {
424  return value.toMap();
425  }
426  else
427  {
428  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to map" ).arg( value.toString() ) );
429  return QVariantMap();
430  }
431  }
432 };
433 
435 
436 #endif // QGSEXPRESSIONUTILS_H
Abstract base class for all nodes that can appear in an expression.
Class for parsing and evaluation of expressions (formerly called "search strings").
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
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:85
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:99
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:500
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:366
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:1856