QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 
191  static double getDoubleValue( const QVariant &value, QgsExpression *parent )
192  {
193  bool ok;
194  double x = value.toDouble( &ok );
195  if ( !ok || std::isnan( x ) || !std::isfinite( x ) )
196  {
197  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to double" ).arg( value.toString() ) );
198  return 0;
199  }
200  return x;
201  }
202 
203  static qlonglong getIntValue( const QVariant &value, QgsExpression *parent )
204  {
205  bool ok;
206  qlonglong x = value.toLongLong( &ok );
207  if ( ok )
208  {
209  return x;
210  }
211  else
212  {
213  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to int" ).arg( value.toString() ) );
214  return 0;
215  }
216  }
217 
218  static int getNativeIntValue( const QVariant &value, QgsExpression *parent )
219  {
220  bool ok;
221  qlonglong x = value.toLongLong( &ok );
222  if ( ok && x >= std::numeric_limits<int>::min() && x <= std::numeric_limits<int>::max() )
223  {
224  return x;
225  }
226  else
227  {
228  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to native int" ).arg( value.toString() ) );
229  return 0;
230  }
231  }
232 
233  static QDateTime getDateTimeValue( const QVariant &value, QgsExpression *parent )
234  {
235  QDateTime d = value.toDateTime();
236  if ( d.isValid() )
237  {
238  return d;
239  }
240  else
241  {
242  QTime t = value.toTime();
243  if ( t.isValid() )
244  {
245  return QDateTime( QDate( 1, 1, 1 ), t );
246  }
247 
248  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( value.toString() ) );
249  return QDateTime();
250  }
251  }
252 
253  static QDate getDateValue( const QVariant &value, QgsExpression *parent )
254  {
255  QDate d = value.toDate();
256  if ( d.isValid() )
257  {
258  return d;
259  }
260  else
261  {
262  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( value.toString() ) );
263  return QDate();
264  }
265  }
266 
267  static QTime getTimeValue( const QVariant &value, QgsExpression *parent )
268  {
269  QTime t = value.toTime();
270  if ( t.isValid() )
271  {
272  return t;
273  }
274  else
275  {
276  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( value.toString() ) );
277  return QTime();
278  }
279  }
280 
281  static QgsInterval getInterval( const QVariant &value, QgsExpression *parent, bool report_error = false )
282  {
283  if ( value.canConvert<QgsInterval>() )
284  return value.value<QgsInterval>();
285 
286  QgsInterval inter = QgsInterval::fromString( value.toString() );
287  if ( inter.isValid() )
288  {
289  return inter;
290  }
291  // If we get here then we can't convert so we just error and return invalid.
292  if ( report_error )
293  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to interval" ).arg( value.toString() ) );
294 
295  return QgsInterval();
296  }
297 
298  static QgsGradientColorRamp getRamp( const QVariant &value, QgsExpression *parent, bool report_error = false )
299  {
300  if ( value.canConvert<QgsGradientColorRamp>() )
301  return value.value<QgsGradientColorRamp>();
302 
303  // If we get here then we can't convert so we just error and return invalid.
304  if ( report_error )
305  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to gradient ramp" ).arg( value.toString() ) );
306 
307  return QgsGradientColorRamp();
308  }
309 
310  static QgsGeometry getGeometry( const QVariant &value, QgsExpression *parent )
311  {
312  if ( value.canConvert<QgsGeometry>() )
313  return value.value<QgsGeometry>();
314 
315  parent->setEvalErrorString( QStringLiteral( "Cannot convert to geometry" ) );
316  return QgsGeometry();
317  }
318 
319  static QgsFeature getFeature( const QVariant &value, QgsExpression *parent )
320  {
321  if ( value.canConvert<QgsFeature>() )
322  return value.value<QgsFeature>();
323 
324  parent->setEvalErrorString( QStringLiteral( "Cannot convert to feature" ) );
325  return 0;
326  }
327 
328  static QgsExpressionNode *getNode( const QVariant &value, QgsExpression *parent )
329  {
330  if ( value.canConvert<QgsExpressionNode *>() )
331  return value.value<QgsExpressionNode *>();
332 
333  parent->setEvalErrorString( QStringLiteral( "Cannot convert to node" ) );
334  return nullptr;
335  }
336 
337  static QgsMapLayer *getMapLayer( const QVariant &value, QgsExpression * )
338  {
339  // First check if we already received a layer pointer
340  QgsMapLayer *ml = value.value< QgsWeakMapLayerPointer >().data();
341  QgsProject *project = QgsProject::instance();
342 
343  // No pointer yet, maybe it's a layer id?
344  if ( !ml )
345  ml = project->mapLayer( value.toString() );
346 
347  // Still nothing? Check for layer name
348  if ( !ml )
349  ml = project->mapLayersByName( value.toString() ).value( 0 );
350 
351  return ml;
352  }
353 
354  static std::unique_ptr<QgsVectorLayerFeatureSource> getFeatureSource( const QVariant &value, QgsExpression *e )
355  {
356  std::unique_ptr<QgsVectorLayerFeatureSource> featureSource;
357 
358  auto getFeatureSource = [ &value, e, &featureSource ]
359  {
360  QgsVectorLayer *layer = getVectorLayer( value, e );
361 
362  if ( layer )
363  {
364  featureSource.reset( new QgsVectorLayerFeatureSource( layer ) );
365  }
366  };
367 
368 #if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
369  // Make sure we only deal with the vector layer on the main thread where it lives.
370  // Anything else risks a crash.
371  if ( QThread::currentThread() == qApp->thread() )
372  getFeatureSource();
373  else
374  QMetaObject::invokeMethod( qApp, getFeatureSource, Qt::BlockingQueuedConnection );
375 #else
376  getFeatureSource();
377 #endif
378 
379  return featureSource;
380  }
381 
382  static QgsVectorLayer *getVectorLayer( const QVariant &value, QgsExpression *e )
383  {
384  return qobject_cast<QgsVectorLayer *>( getMapLayer( value, e ) );
385  }
386 
387  static QgsRasterLayer *getRasterLayer( const QVariant &value, QgsExpression *e )
388  {
389  return qobject_cast<QgsRasterLayer *>( getMapLayer( value, e ) );
390  }
391 
392  static QVariantList getListValue( const QVariant &value, QgsExpression *parent )
393  {
394  if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
395  {
396  return value.toList();
397  }
398  else
399  {
400  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to array" ).arg( value.toString() ) );
401  return QVariantList();
402  }
403  }
404 
405  static QVariantMap getMapValue( const QVariant &value, QgsExpression *parent )
406  {
407  if ( value.type() == QVariant::Map )
408  {
409  return value.toMap();
410  }
411  else
412  {
413  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to map" ).arg( value.toString() ) );
414  return QVariantMap();
415  }
416  }
417 };
418 
420 
421 #endif // QGSEXPRESSIONUTILS_H
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:183
Class for parsing and evaluation of expressions (formerly called "search strings").
Base class for all map layer types.
Definition: qgsmaplayer.h:79
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:280
static QgsInterval fromString(const QString &string)
Converts a string to an interval.
Definition: qgsinterval.cpp:46
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:1632
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
double value(int index) const override
Returns relative value between [0,1] of color at specified index.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:89
Abstract base class for all nodes that can appear in an expression.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
Partial snapshot of vector layer&#39;s state (only the members necessary for access to features) ...
A representation of the interval between two datetime values.
Definition: qgsinterval.h:39
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:442
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:139
Represents a vector layer which manages a vector based data sets.