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