QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsexpressionfunction.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsexpressionfunction.cpp
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 #include "qgscoordinateformatter.h"
17 #include "qgsexpressionfunction.h"
18 #include "qgsexpressionutils.h"
19 #include "qgsexpressionnodeimpl.h"
20 #include "qgsfeaturerequest.h"
21 #include "qgsstringutils.h"
22 #include "qgsmultipoint.h"
23 #include "qgsgeometryutils.h"
24 #include "qgshstoreutils.h"
25 #include "qgsmultilinestring.h"
26 #include "qgslinestring.h"
27 #include "qgscurvepolygon.h"
29 #include "qgspolygon.h"
30 #include "qgstriangle.h"
31 #include "qgscurve.h"
32 #include "qgsregularpolygon.h"
33 #include "qgsquadrilateral.h"
34 #include "qgsmultipolygon.h"
35 #include "qgsogcutils.h"
36 #include "qgsdistancearea.h"
37 #include "qgsgeometryengine.h"
38 #include "qgsexpressionsorter.h"
39 #include "qgssymbollayerutils.h"
40 #include "qgsstyle.h"
41 #include "qgsexception.h"
42 #include "qgsmessagelog.h"
43 #include "qgsrasterlayer.h"
44 #include "qgsvectorlayer.h"
45 #include "qgsrasterbandstats.h"
46 #include "qgscolorramp.h"
48 #include "qgsfieldformatter.h"
50 #include "qgsproviderregistry.h"
51 #include "sqlite3.h"
52 #include "qgstransaction.h"
53 #include "qgsthreadingutils.h"
54 #include "qgsapplication.h"
55 #include "qgis.h"
57 
58 
59 const QString QgsExpressionFunction::helpText() const
60 {
61  return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
62 }
63 
65 {
66  Q_UNUSED( node )
67  // evaluate arguments
68  QVariantList argValues;
69  if ( args )
70  {
71  int arg = 0;
72  const QList< QgsExpressionNode * > argList = args->list();
73  for ( QgsExpressionNode *n : argList )
74  {
75  QVariant v;
76  if ( lazyEval() )
77  {
78  // Pass in the node for the function to eval as it needs.
79  v = QVariant::fromValue( n );
80  }
81  else
82  {
83  v = n->eval( parent, context );
85  bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
86  if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
87  return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
88  }
89  argValues.append( v );
90  arg++;
91  }
92  }
93 
94  return func( argValues, context, parent, node );
95 }
96 
98 {
99  Q_UNUSED( node )
100  return true;
101 }
102 
104 {
105  return QStringList();
106 }
107 
109 {
110  Q_UNUSED( parent )
111  Q_UNUSED( context )
112  Q_UNUSED( node )
113  return false;
114 }
115 
117 {
118  Q_UNUSED( parent )
119  Q_UNUSED( context )
120  Q_UNUSED( node )
121  return true;
122 }
123 
125 {
126  Q_UNUSED( node )
127  return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
128 }
129 
131 {
132  return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
133 }
134 
136 {
137  return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
138 }
139 
141 {
142  return mHandlesNull;
143 }
144 
145 // doxygen doesn't like this constructor for some reason (maybe the function arguments?)
148  FcnEval fcn,
149  const QString &group,
150  const QString &helpText,
151  const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
152  const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
153  bool lazyEval,
154  const QStringList &aliases,
155  bool handlesNull )
157  , mFnc( fcn )
158  , mAliases( aliases )
159  , mUsesGeometry( false )
160  , mUsesGeometryFunc( usesGeometry )
161  , mReferencedColumnsFunc( referencedColumns )
162 {
163 }
165 
167 {
168  return mAliases;
169 }
170 
172 {
173  if ( mUsesGeometryFunc )
174  return mUsesGeometryFunc( node );
175  else
176  return mUsesGeometry;
177 }
178 
180 {
181  if ( mReferencedColumnsFunc )
182  return mReferencedColumnsFunc( node );
183  else
184  return mReferencedColumns;
185 }
186 
188 {
189  if ( mIsStaticFunc )
190  return mIsStaticFunc( node, parent, context );
191  else
192  return mIsStatic;
193 }
194 
196 {
197  if ( mPrepareFunc )
198  return mPrepareFunc( node, parent, context );
199 
200  return true;
201 }
202 
204 {
205  mIsStaticFunc = isStatic;
206 }
207 
209 {
210  mIsStaticFunc = nullptr;
211  mIsStatic = isStatic;
212 }
213 
214 void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
215 {
216  mPrepareFunc = prepareFunc;
217 }
218 
220 {
221  if ( node && node->args() )
222  {
223  const QList< QgsExpressionNode * > argList = node->args()->list();
224  for ( QgsExpressionNode *argNode : argList )
225  {
226  if ( !argNode->isStatic( parent, context ) )
227  return false;
228  }
229  }
230 
231  return true;
232 }
233 
234 static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
235 {
236  double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
237  double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
238  double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
239 
240  if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
241  return QVariant();
242 
243  QVariantList array;
244  int length = 1;
245 
246  array << start;
247  double current = start + step;
248  while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
249  {
250  array << current;
251  current += step;
252  length++;
253  }
254 
255  return array;
256 }
257 
258 static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
259 {
260  if ( !context )
261  return QVariant();
262 
263  QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
264  return context->variable( name );
265 }
266 
267 static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
268 {
269  if ( !context )
270  return QVariant();
271 
272  QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
273  QgsExpression expression( expString );
274  return expression.evaluate( context );
275 }
276 
277 static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
278 {
279  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
280  return QVariant( std::sqrt( x ) );
281 }
282 
283 static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
284 {
285  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
286  return QVariant( std::fabs( val ) );
287 }
288 
289 static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
290 {
291  double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
292  return ( deg * M_PI ) / 180;
293 }
294 static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
295 {
296  double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
297  return ( 180 * rad ) / M_PI;
298 }
299 static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
300 {
301  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
302  return QVariant( std::sin( x ) );
303 }
304 static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
305 {
306  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
307  return QVariant( std::cos( x ) );
308 }
309 static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
310 {
311  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
312  return QVariant( std::tan( x ) );
313 }
314 static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
315 {
316  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
317  return QVariant( std::asin( x ) );
318 }
319 static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
320 {
321  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
322  return QVariant( std::acos( x ) );
323 }
324 static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
325 {
326  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
327  return QVariant( std::atan( x ) );
328 }
329 static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
330 {
331  double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
332  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
333  return QVariant( std::atan2( y, x ) );
334 }
335 static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
336 {
337  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
338  return QVariant( std::exp( x ) );
339 }
340 static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
341 {
342  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
343  if ( x <= 0 )
344  return QVariant();
345  return QVariant( std::log( x ) );
346 }
347 static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
348 {
349  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
350  if ( x <= 0 )
351  return QVariant();
352  return QVariant( log10( x ) );
353 }
354 static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
355 {
356  double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
357  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
358  if ( x <= 0 || b <= 0 )
359  return QVariant();
360  return QVariant( std::log( x ) / std::log( b ) );
361 }
362 static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
363 {
364  double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
365  double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
366  if ( max < min )
367  return QVariant();
368 
369  // Return a random double in the range [min, max] (inclusive)
370  double f = static_cast< double >( qrand() ) / RAND_MAX;
371  return QVariant( min + f * ( max - min ) );
372 }
373 static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
374 {
375  qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
376  qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
377  if ( max < min )
378  return QVariant();
379 
380  // Return a random integer in the range [min, max] (inclusive)
381  return QVariant( min + ( qrand() % static_cast< qlonglong >( max - min + 1 ) ) );
382 }
383 
384 static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
385 {
386  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
387  double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
388  double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
389  double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
390  double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
391 
392  if ( domainMin >= domainMax )
393  {
394  parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
395  return QVariant();
396  }
397 
398  // outside of domain?
399  if ( val >= domainMax )
400  {
401  return rangeMax;
402  }
403  else if ( val <= domainMin )
404  {
405  return rangeMin;
406  }
407 
408  // calculate linear scale
409  double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
410  double c = rangeMin - ( domainMin * m );
411 
412  // Return linearly scaled value
413  return QVariant( m * val + c );
414 }
415 
416 static QVariant fcnExpScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
417 {
418  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
419  double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
420  double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
421  double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
422  double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
423  double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
424 
425  if ( domainMin >= domainMax )
426  {
427  parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
428  return QVariant();
429  }
430  if ( exponent <= 0 )
431  {
432  parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
433  return QVariant();
434  }
435 
436  // outside of domain?
437  if ( val >= domainMax )
438  {
439  return rangeMax;
440  }
441  else if ( val <= domainMin )
442  {
443  return rangeMin;
444  }
445 
446  // Return exponentially scaled value
447  return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
448 }
449 
450 static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
451 {
452  QVariant result( QVariant::Double );
453  double maxVal = std::numeric_limits<double>::quiet_NaN();
454  for ( const QVariant &val : values )
455  {
456  double testVal = val.isNull() ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
457  if ( std::isnan( maxVal ) )
458  {
459  maxVal = testVal;
460  }
461  else if ( !std::isnan( testVal ) )
462  {
463  maxVal = std::max( maxVal, testVal );
464  }
465  }
466 
467  if ( !std::isnan( maxVal ) )
468  {
469  result = QVariant( maxVal );
470  }
471  return result;
472 }
473 
474 static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
475 {
476  QVariant result( QVariant::Double );
477  double minVal = std::numeric_limits<double>::quiet_NaN();
478  for ( const QVariant &val : values )
479  {
480  double testVal = val.isNull() ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
481  if ( std::isnan( minVal ) )
482  {
483  minVal = testVal;
484  }
485  else if ( !std::isnan( testVal ) )
486  {
487  minVal = std::min( minVal, testVal );
488  }
489  }
490 
491  if ( !std::isnan( minVal ) )
492  {
493  result = QVariant( minVal );
494  }
495  return result;
496 }
497 
498 static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
499 {
500  //lazy eval, so we need to evaluate nodes now
501 
502  //first node is layer id or name
503  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
505  QVariant value = node->eval( parent, context );
507  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, parent );
508  if ( !vl )
509  {
510  parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
511  return QVariant();
512  }
513 
514  // second node is aggregate type
515  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
517  value = node->eval( parent, context );
519  bool ok = false;
520  QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
521  if ( !ok )
522  {
523  parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
524  return QVariant();
525  }
526 
527  // third node is subexpression (or field name)
528  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
530  QString subExpression = node->dump();
531 
533  //optional forth node is filter
534  if ( values.count() > 3 )
535  {
536  node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
538  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
539  if ( !nl || nl->value().isValid() )
540  parameters.filter = node->dump();
541  }
542 
543  //optional fifth node is concatenator
544  if ( values.count() > 4 )
545  {
546  node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
548  value = node->eval( parent, context );
550  parameters.delimiter = value.toString();
551  }
552 
553  //optional sixth node is order by
554  QString orderBy;
555  if ( values.count() > 5 )
556  {
557  node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
559  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
560  if ( !nl || nl->value().isValid() )
561  {
562  orderBy = node->dump();
563  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
564  }
565  }
566 
567  QVariant result;
568  if ( context )
569  {
570  QString cacheKey;
571  QgsExpression subExp( subExpression );
572  QgsExpression filterExp( parameters.filter );
573  if ( filterExp.referencedVariables().contains( QStringLiteral( "parent" ) )
574  || filterExp.referencedVariables().contains( QString() )
575  || subExp.referencedVariables().contains( QStringLiteral( "parent" ) )
576  || subExp.referencedVariables().contains( QString() ) )
577  {
578  cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter,
579  QString::number( context->feature().id() ), QString( qHash( context->feature() ) ), orderBy );
580  }
581  else
582  {
583  cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter, orderBy );
584  }
585 
586  if ( context && context->hasCachedValue( cacheKey ) )
587  return context->cachedValue( cacheKey );
588 
589  QgsExpressionContext subContext( *context );
591  subScope->setVariable( QStringLiteral( "parent" ), context->feature() );
592  subContext.appendScope( subScope );
593  result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok );
594 
595  context->setCachedValue( cacheKey, result );
596  }
597  else
598  {
599  result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok );
600  }
601  if ( !ok )
602  {
603  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
604  return QVariant();
605  }
606 
607  return result;
608 }
609 
610 static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
611 {
612  if ( !context )
613  {
614  parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
615  return QVariant();
616  }
617 
618  // first step - find current layer
619  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
620  if ( !vl )
621  {
622  parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
623  return QVariant();
624  }
625 
626  //lazy eval, so we need to evaluate nodes now
627 
628  //first node is relation name
629  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
631  QVariant value = node->eval( parent, context );
633  QString relationId = value.toString();
634  // check relation exists
635  QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId );
636  if ( !relation.isValid() || relation.referencedLayer() != vl )
637  {
638  // check for relations by name
639  QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId );
640  if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
641  {
642  parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
643  return QVariant();
644  }
645  else
646  {
647  relation = relations.at( 0 );
648  }
649  }
650 
651  QgsVectorLayer *childLayer = relation.referencingLayer();
652 
653  // second node is aggregate type
654  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
656  value = node->eval( parent, context );
658  bool ok = false;
659  QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
660  if ( !ok )
661  {
662  parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
663  return QVariant();
664  }
665 
666  //third node is subexpression (or field name)
667  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
669  QString subExpression = node->dump();
670 
671  //optional fourth node is concatenator
673  if ( values.count() > 3 )
674  {
675  node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
677  value = node->eval( parent, context );
679  parameters.delimiter = value.toString();
680  }
681 
682  //optional fifth node is order by
683  QString orderBy;
684  if ( values.count() > 4 )
685  {
686  node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
688  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
689  if ( !nl || nl->value().isValid() )
690  {
691  orderBy = node->dump();
692  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
693  }
694  }
695 
696 
697  FEAT_FROM_CONTEXT( context, f )
698  parameters.filter = relation.getRelatedFeaturesFilter( f );
699 
700  QString cacheKey = QStringLiteral( "relagg:%1:%2:%3:%4:%5" ).arg( vl->id(),
701  QString::number( static_cast< int >( aggregate ) ),
702  subExpression,
703  parameters.filter,
704  orderBy );
705  if ( context && context->hasCachedValue( cacheKey ) )
706  return context->cachedValue( cacheKey );
707 
708  QVariant result;
709  ok = false;
710 
711 
712  QgsExpressionContext subContext( *context );
713  result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok );
714 
715  if ( !ok )
716  {
717  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
718  return QVariant();
719  }
720 
721  // cache value
722  if ( context )
723  context->setCachedValue( cacheKey, result );
724  return result;
725 }
726 
727 
728 static QVariant fcnAggregateGeneric( QgsAggregateCalculator::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
729 {
730  if ( !context )
731  {
732  parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
733  return QVariant();
734  }
735 
736  // first step - find current layer
737  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
738  if ( !vl )
739  {
740  parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
741  return QVariant();
742  }
743 
744  //lazy eval, so we need to evaluate nodes now
745 
746  //first node is subexpression (or field name)
747  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
749  QString subExpression = node->dump();
750 
751  //optional second node is group by
752  QString groupBy;
753  if ( values.count() > 1 )
754  {
755  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
757  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
758  if ( !nl || nl->value().isValid() )
759  groupBy = node->dump();
760  }
761 
762  //optional third node is filter
763  if ( values.count() > 2 )
764  {
765  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
767  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
768  if ( !nl || nl->value().isValid() )
769  parameters.filter = node->dump();
770  }
771 
772  //optional order by node, if supported
773  QString orderBy;
774  if ( orderByPos >= 0 && values.count() > orderByPos )
775  {
776  node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
778  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
779  if ( !nl || nl->value().isValid() )
780  {
781  orderBy = node->dump();
782  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
783  }
784  }
785 
786  // build up filter with group by
787 
788  // find current group by value
789  if ( !groupBy.isEmpty() )
790  {
791  QgsExpression groupByExp( groupBy );
792  QVariant groupByValue = groupByExp.evaluate( context );
793  QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
794  groupByValue.isNull() ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
795  QgsExpression::quotedValue( groupByValue ) );
796  if ( !parameters.filter.isEmpty() )
797  parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
798  else
799  parameters.filter = groupByClause;
800  }
801 
802  QString cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(),
803  QString::number( static_cast< int >( aggregate ) ),
804  subExpression,
805  parameters.filter,
806  orderBy );
807  if ( context && context->hasCachedValue( cacheKey ) )
808  return context->cachedValue( cacheKey );
809 
810  QVariant result;
811  bool ok = false;
812 
813  QgsExpressionContext subContext( *context );
814  result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok );
815 
816  if ( !ok )
817  {
818  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
819  return QVariant();
820  }
821 
822  // cache value
823  if ( context )
824  context->setCachedValue( cacheKey, result );
825  return result;
826 }
827 
828 
829 static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
830 {
831  return fcnAggregateGeneric( QgsAggregateCalculator::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
832 }
833 
834 static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
835 {
836  return fcnAggregateGeneric( QgsAggregateCalculator::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
837 }
838 
839 static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
840 {
841  return fcnAggregateGeneric( QgsAggregateCalculator::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
842 }
843 
844 static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
845 {
846  return fcnAggregateGeneric( QgsAggregateCalculator::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
847 }
848 
849 static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
850 {
851  return fcnAggregateGeneric( QgsAggregateCalculator::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
852 }
853 
854 static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
855 {
856  return fcnAggregateGeneric( QgsAggregateCalculator::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
857 }
858 
859 static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
860 {
861  return fcnAggregateGeneric( QgsAggregateCalculator::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
862 }
863 
864 static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
865 {
866  return fcnAggregateGeneric( QgsAggregateCalculator::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
867 }
868 
869 static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
870 {
871  return fcnAggregateGeneric( QgsAggregateCalculator::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
872 }
873 
874 static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
875 {
876  return fcnAggregateGeneric( QgsAggregateCalculator::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
877 }
878 
879 static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
880 {
881  return fcnAggregateGeneric( QgsAggregateCalculator::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
882 }
883 
884 static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
885 {
886  return fcnAggregateGeneric( QgsAggregateCalculator::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
887 }
888 
889 static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
890 {
891  return fcnAggregateGeneric( QgsAggregateCalculator::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
892 }
893 
894 static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
895 {
896  return fcnAggregateGeneric( QgsAggregateCalculator::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
897 }
898 
899 static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
900 {
901  return fcnAggregateGeneric( QgsAggregateCalculator::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
902 }
903 
904 static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
905 {
906  return fcnAggregateGeneric( QgsAggregateCalculator::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
907 }
908 
909 static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
910 {
911  return fcnAggregateGeneric( QgsAggregateCalculator::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
912 }
913 
914 static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
915 {
916  return fcnAggregateGeneric( QgsAggregateCalculator::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
917 }
918 
919 static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
920 {
922 
923  //fourth node is concatenator
924  if ( values.count() > 3 )
925  {
926  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
928  QVariant value = node->eval( parent, context );
930  parameters.delimiter = value.toString();
931  }
932 
933  return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenate, values, parameters, context, parent, 4 );
934 }
935 
936 static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
937 {
939 
940  //fourth node is concatenator
941  if ( values.count() > 3 )
942  {
943  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
945  QVariant value = node->eval( parent, context );
947  parameters.delimiter = value.toString();
948  }
949 
950  return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenateUnique, values, parameters, context, parent, 4 );
951 }
952 
953 static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
954 {
955  return fcnAggregateGeneric( QgsAggregateCalculator::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
956 }
957 
958 static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
959 {
960  if ( !context )
961  return QVariant();
962 
963  QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
964  bool ok = false;
965  if ( !scale.isValid() || scale.isNull() )
966  return QVariant();
967 
968  const double v = scale.toDouble( &ok );
969  if ( ok )
970  return v;
971  return QVariant();
972 }
973 
974 static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
975 {
976  double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
977  double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
978  double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
979 
980  // force testValue to sit inside the range specified by the min and max value
981  if ( testValue <= minValue )
982  {
983  return QVariant( minValue );
984  }
985  else if ( testValue >= maxValue )
986  {
987  return QVariant( maxValue );
988  }
989  else
990  {
991  return QVariant( testValue );
992  }
993 }
994 
995 static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
996 {
997  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
998  return QVariant( std::floor( x ) );
999 }
1000 
1001 static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1002 {
1003  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1004  return QVariant( std::ceil( x ) );
1005 }
1006 
1007 static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1008 {
1009  return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1010 }
1011 static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1012 {
1013  return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1014 }
1015 static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1016 {
1017  return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1018 }
1019 
1020 static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1021 {
1022  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1023  if ( format.isEmpty() )
1024  return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1025 
1026  QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1027  QDateTime datetime = QDateTime::fromString( datetimestring, format );
1028  if ( !datetime.isValid() )
1029  {
1030  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1031  datetime = QDateTime();
1032  }
1033  return QVariant( datetime );
1034 }
1035 
1036 static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1037 {
1038  for ( const QVariant &value : values )
1039  {
1040  if ( value.isNull() )
1041  continue;
1042  return value;
1043  }
1044  return QVariant();
1045 }
1046 
1047 static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1048 {
1049  const QVariant val1 = values.at( 0 );
1050  const QVariant val2 = values.at( 1 );
1051 
1052  if ( val1 == val2 )
1053  return QVariant();
1054  else
1055  return val1;
1056 }
1057 
1058 static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1059 {
1060  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1061  return QVariant( str.toLower() );
1062 }
1063 static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1064 {
1065  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1066  return QVariant( str.toUpper() );
1067 }
1068 static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1069 {
1070  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1071  QStringList elems = str.split( ' ' );
1072  for ( int i = 0; i < elems.size(); i++ )
1073  {
1074  if ( elems[i].size() > 1 )
1075  elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1076  }
1077  return QVariant( elems.join( QStringLiteral( " " ) ) );
1078 }
1079 
1080 static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1081 {
1082  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1083  return QVariant( str.trimmed() );
1084 }
1085 
1086 static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1087 {
1088  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1089  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1090  return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1091 }
1092 
1093 static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1094 {
1095  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1096  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1097  return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1098 }
1099 
1100 static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1101 {
1102  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1103  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1104  int dist = QgsStringUtils::hammingDistance( string1, string2 );
1105  return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1106 }
1107 
1108 static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1109 {
1110  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1111  return QVariant( QgsStringUtils::soundex( string ) );
1112 }
1113 
1114 static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1115 {
1116  QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1117  return QVariant( QString( character ) );
1118 }
1119 
1120 static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1121 {
1122  if ( values.length() == 2 || values.length() == 3 )
1123  {
1124  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1125  qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1126 
1127  QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1128 
1129  return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1130  }
1131 
1132  return QVariant();
1133 }
1134 
1135 static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1136 {
1137  // two variants, one for geometry, one for string
1138  if ( values.at( 0 ).canConvert<QgsGeometry>() )
1139  {
1140  //geometry variant
1141  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1142  if ( geom.type() != QgsWkbTypes::LineGeometry )
1143  return QVariant();
1144 
1145  return QVariant( geom.length() );
1146  }
1147 
1148  //otherwise fall back to string variant
1149  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1150  return QVariant( str.length() );
1151 }
1152 
1153 static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1154 {
1155  if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
1156  {
1157  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1158  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1159 
1160  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1161  {
1162  str = str.replace( it.key(), it.value().toString() );
1163  }
1164 
1165  return QVariant( str );
1166  }
1167  else if ( values.count() == 3 )
1168  {
1169  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1170  QVariantList before;
1171  QVariantList after;
1172  bool isSingleReplacement = false;
1173 
1174  if ( values.at( 1 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
1175  {
1176  before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1177  }
1178  else
1179  {
1180  before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1181  }
1182 
1183  if ( values.at( 2 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
1184  {
1185  after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1186  isSingleReplacement = true;
1187  }
1188  else
1189  {
1190  after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1191  }
1192 
1193  if ( !isSingleReplacement && before.length() != after.length() )
1194  {
1195  parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1196  return QVariant();
1197  }
1198 
1199  for ( int i = 0; i < before.length(); i++ )
1200  {
1201  str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1202  }
1203 
1204  return QVariant( str );
1205  }
1206  else
1207  {
1208  parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1209  return QVariant();
1210  }
1211 }
1212 static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1213 {
1214  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1215  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1216  QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1217 
1218  QRegularExpression re( regexp );
1219  if ( !re.isValid() )
1220  {
1221  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1222  return QVariant();
1223  }
1224  return QVariant( str.replace( re, after ) );
1225 }
1226 
1227 static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1228 {
1229  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1230  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1231 
1232  QRegularExpression re( regexp );
1233  if ( !re.isValid() )
1234  {
1235  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1236  return QVariant();
1237  }
1238  return QVariant( ( str.indexOf( re ) + 1 ) );
1239 }
1240 
1241 static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1242 {
1243  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1244  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1245  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1246 
1247  QRegularExpression re( regexp );
1248  if ( !re.isValid() )
1249  {
1250  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1251  return QVariant();
1252  }
1253 
1254  QRegularExpressionMatch matches = re.match( str );
1255  if ( matches.hasMatch() )
1256  {
1257  QVariantList array;
1258  QStringList list = matches.capturedTexts();
1259 
1260  // Skip the first string to only return captured groups
1261  for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1262  {
1263  array += ( !( *it ).isEmpty() ) ? *it : empty;
1264  }
1265 
1266  return QVariant( array );
1267  }
1268  else
1269  {
1270  return QVariant();
1271  }
1272 }
1273 
1274 static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1275 {
1276  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1277  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1278 
1279  QRegularExpression re( regexp );
1280  if ( !re.isValid() )
1281  {
1282  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1283  return QVariant();
1284  }
1285 
1286  // extract substring
1287  QRegularExpressionMatch match = re.match( str );
1288  if ( match.hasMatch() )
1289  {
1290  // return first capture
1291  if ( match.lastCapturedIndex() > 0 )
1292  {
1293  // a capture group was present, so use that
1294  return QVariant( match.captured( 1 ) );
1295  }
1296  else
1297  {
1298  // no capture group, so using all match
1299  return QVariant( match.captured( 0 ) );
1300  }
1301  }
1302  else
1303  {
1304  return QVariant( "" );
1305  }
1306 }
1307 
1308 static QVariant fcnUuid( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1309 {
1310  return QUuid::createUuid().toString();
1311 }
1312 
1313 static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1314 {
1315  if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1316  return QVariant();
1317 
1318  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1319  int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1320 
1321  int len = 0;
1322  if ( values.at( 2 ).isValid() )
1323  len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1324  else
1325  len = str.size();
1326 
1327  if ( from < 0 )
1328  {
1329  from = str.size() + from;
1330  if ( from < 0 )
1331  {
1332  from = 0;
1333  }
1334  }
1335  else if ( from > 0 )
1336  {
1337  //account for the fact that substr() starts at 1
1338  from -= 1;
1339  }
1340 
1341  if ( len < 0 )
1342  {
1343  len = str.size() + len - from;
1344  if ( len < 0 )
1345  {
1346  len = 0;
1347  }
1348  }
1349 
1350  return QVariant( str.mid( from, len ) );
1351 }
1352 static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1353 {
1354  FEAT_FROM_CONTEXT( context, f )
1355  // TODO: handling of 64-bit feature ids?
1356  return QVariant( static_cast< int >( f.id() ) );
1357 }
1358 
1359 static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1360 {
1361  QgsRasterLayer *layer = QgsExpressionUtils::getRasterLayer( values.at( 0 ), parent );
1362  if ( !layer || !layer->dataProvider() )
1363  {
1364  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1365  return QVariant();
1366  }
1367 
1368  int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1369  if ( bandNb < 1 || bandNb > layer->bandCount() )
1370  {
1371  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1372  return QVariant();
1373  }
1374 
1375  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1376  if ( geom.isNull() || geom.type() != QgsWkbTypes::PointGeometry )
1377  {
1378  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1379  return QVariant();
1380  }
1381 
1382  QgsPointXY point = geom.asPoint();
1383  if ( geom.isMultipart() )
1384  {
1385  QgsMultiPointXY multiPoint = geom.asMultiPoint();
1386  if ( multiPoint.count() == 1 )
1387  {
1388  point = multiPoint[0];
1389  }
1390  else
1391  {
1392  // if the geometry contains more than one part, return an undefined value
1393  return QVariant();
1394  }
1395  }
1396 
1397  double value = layer->dataProvider()->sample( point, bandNb );
1398  return std::isnan( value ) ? QVariant() : value;
1399 }
1400 
1401 static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1402 {
1403  if ( !context )
1404  return QVariant();
1405 
1406  return context->feature();
1407 }
1408 
1409 static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1410 {
1411  QgsFeature feature;
1412  QString attr;
1413  if ( values.size() == 1 )
1414  {
1415  attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1416  feature = context->feature();
1417  }
1418  else if ( values.size() == 2 )
1419  {
1420  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1421  attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1422  }
1423  else
1424  {
1425  parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %1 given." ).arg( values.length() ) );
1426  return QVariant();
1427  }
1428 
1429  return feature.attribute( attr );
1430 }
1431 
1432 static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1433 {
1434  QgsFeature feature;
1435  QString attr;
1436  if ( values.size() == 0 || values.at( 0 ).isNull() )
1437  {
1438  feature = context->feature();
1439  }
1440  else
1441  {
1442  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1443  }
1444 
1445  const QgsFields fields = feature.fields();
1446  QVariantMap result;
1447  for ( int i = 0; i < fields.count(); ++i )
1448  {
1449  result.insert( fields.at( i ).name(), feature.attribute( i ) );
1450  }
1451  return result;
1452 }
1453 
1454 static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1455 {
1456  QgsVectorLayer *layer = nullptr;
1457  QgsFeature feature;
1458 
1459  if ( values.isEmpty() )
1460  {
1461  feature = context->feature();
1462  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1463  }
1464  else if ( values.size() == 1 )
1465  {
1466  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1467  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1468  }
1469  else if ( values.size() == 2 )
1470  {
1471  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1472  feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1473  }
1474  else
1475  {
1476  parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %1 given." ).arg( values.length() ) );
1477  return QVariant();
1478  }
1479 
1480  if ( !layer || !feature.isValid() )
1481  {
1482  return QVariant( QVariant::Bool );
1483  }
1484 
1485  return layer->selectedFeatureIds().contains( feature.id() );
1486 }
1487 
1488 static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1489 {
1490  QgsVectorLayer *layer = nullptr;
1491 
1492  if ( values.isEmpty() )
1493  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1494  else if ( values.count() == 1 )
1495  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1496  else
1497  {
1498  parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %1 given." ).arg( values.length() ) );
1499  return QVariant();
1500  }
1501 
1502  if ( !layer )
1503  {
1504  return QVariant( QVariant::LongLong );
1505  }
1506 
1507  return layer->selectedFeatureCount();
1508 }
1509 
1510 static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1511 {
1512  static QMap<QString, qlonglong> counterCache;
1513  QVariant functionResult;
1514 
1515  std::function<void()> fetchAndIncrementFunc = [ =, &functionResult ]()
1516  {
1517  QString database;
1518  const QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1519 
1520  if ( layer )
1521  {
1522  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
1523  database = decodedUri.value( QStringLiteral( "path" ) ).toString();
1524  if ( database.isEmpty() )
1525  {
1526  parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
1527  }
1528  }
1529  else
1530  {
1531  database = values.at( 0 ).toString();
1532  }
1533 
1534  const QString table = values.at( 1 ).toString();
1535  const QString idColumn = values.at( 2 ).toString();
1536  const QString filterAttribute = values.at( 3 ).toString();
1537  const QVariant filterValue = values.at( 4 ).toString();
1538  const QVariantMap defaultValues = values.at( 5 ).toMap();
1539 
1540  // read from database
1541  sqlite3_database_unique_ptr sqliteDb;
1542  sqlite3_statement_unique_ptr sqliteStatement;
1543 
1544  if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
1545  {
1546  parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
1547  functionResult = QVariant();
1548  return;
1549  }
1550 
1551  QString errorMessage;
1552  QString currentValSql;
1553 
1554  qlonglong nextId = 0;
1555  bool cachedMode = false;
1556  bool valueRetrieved = false;
1557 
1558  QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
1559 
1560  // Running in transaction mode, check for cached value first
1561  if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
1562  {
1563  cachedMode = true;
1564 
1565  auto cachedCounter = counterCache.find( cacheString );
1566 
1567  if ( cachedCounter != counterCache.end() )
1568  {
1569  qlonglong &cachedValue = cachedCounter.value();
1570  nextId = cachedValue;
1571  nextId += 1;
1572  cachedValue = nextId;
1573  valueRetrieved = true;
1574  }
1575  }
1576 
1577  // Either not in cached mode or no cached value found, obtain from DB
1578  if ( !cachedMode || !valueRetrieved )
1579  {
1580  int result = SQLITE_ERROR;
1581 
1582  currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
1583  if ( !filterAttribute.isNull() )
1584  {
1585  currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
1586  }
1587 
1588  sqliteStatement = sqliteDb.prepare( currentValSql, result );
1589 
1590  if ( result == SQLITE_OK )
1591  {
1592  nextId = 0;
1593  if ( sqliteStatement.step() == SQLITE_ROW )
1594  {
1595  nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
1596  }
1597 
1598  // If in cached mode: add value to cache and connect to transaction
1599  if ( cachedMode && result == SQLITE_OK )
1600  {
1601  counterCache.insert( cacheString, nextId );
1602 
1603  QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
1604  {
1605  counterCache.remove( cacheString );
1606  } );
1607  }
1608  valueRetrieved = true;
1609  }
1610  }
1611 
1612  if ( valueRetrieved )
1613  {
1614  QString upsertSql;
1615  upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
1616  QStringList cols;
1617  QStringList vals;
1618  cols << QgsSqliteUtils::quotedIdentifier( idColumn );
1619  vals << QgsSqliteUtils::quotedValue( nextId );
1620 
1621  if ( !filterAttribute.isNull() )
1622  {
1623  cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
1624  vals << QgsSqliteUtils::quotedValue( filterValue );
1625  }
1626 
1627  for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
1628  {
1629  cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
1630  vals << iter.value().toString();
1631  }
1632 
1633  upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
1634  upsertSql += QLatin1String( " VALUES " );
1635  upsertSql += '(' + vals.join( ',' ) + ')';
1636 
1637  int result = SQLITE_ERROR;
1638  if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
1639  {
1640  QgsTransaction *transaction = layer->dataProvider()->transaction();
1641  if ( transaction->executeSql( upsertSql, errorMessage ) )
1642  {
1643  result = SQLITE_OK;
1644  }
1645  }
1646  else
1647  {
1648  result = sqliteDb.exec( upsertSql, errorMessage );
1649  }
1650  if ( result == SQLITE_OK )
1651  {
1652  functionResult = QVariant( nextId );
1653  return;
1654  }
1655  else
1656  {
1657  parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
1658  functionResult = QVariant();
1659  return;
1660  }
1661  }
1662 
1663  functionResult = QVariant();
1664  };
1665 
1666  QgsThreadingUtils::runOnMainThread( fetchAndIncrementFunc );
1667 
1668  return functionResult;
1669 }
1670 
1671 static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1672 {
1673  QString concat;
1674  for ( const QVariant &value : values )
1675  {
1676  concat += QgsExpressionUtils::getStringValue( value, parent );
1677  }
1678  return concat;
1679 }
1680 
1681 static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1682 {
1683  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1684  return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
1685 }
1686 
1687 static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1688 {
1689  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1690  int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1691  return string.right( pos );
1692 }
1693 
1694 static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1695 {
1696  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1697  int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1698  return string.left( pos );
1699 }
1700 
1701 static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1702 {
1703  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1704  int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1705  QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1706  return string.leftJustified( length, fill.at( 0 ), true );
1707 }
1708 
1709 static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1710 {
1711  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1712  int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1713  QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1714  return string.rightJustified( length, fill.at( 0 ), true );
1715 }
1716 
1717 static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1718 {
1719  if ( values.size() < 1 )
1720  {
1721  parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
1722  return QVariant();
1723  }
1724 
1725  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1726  for ( int n = 1; n < values.length(); n++ )
1727  {
1728  string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
1729  }
1730  return string;
1731 }
1732 
1733 
1734 static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1735 {
1736  return QVariant( QDateTime::currentDateTime() );
1737 }
1738 
1739 static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1740 {
1741  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1742  if ( format.isEmpty() )
1743  return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
1744 
1745  QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1746  QDate date = QDate::fromString( datestring, format );
1747  if ( !date.isValid() )
1748  {
1749  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
1750  date = QDate();
1751  }
1752  return QVariant( date );
1753 }
1754 
1755 static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1756 {
1757  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1758  if ( format.isEmpty() )
1759  return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
1760 
1761  QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1762  QTime time = QTime::fromString( timestring, format );
1763  if ( !time.isValid() )
1764  {
1765  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
1766  time = QTime();
1767  }
1768  return QVariant( time );
1769 }
1770 
1771 static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1772 {
1773  return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
1774 }
1775 
1776 /*
1777  * DMS functions
1778  */
1779 
1780 static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1781 {
1782  double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1783  QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1784  int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1785 
1786  QString formatString;
1787  if ( values.count() > 3 )
1788  formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
1789 
1790  QgsCoordinateFormatter::FormatFlags flags = nullptr;
1791  if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
1792  {
1794  }
1795  else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
1796  {
1798  }
1799  else if ( ! formatString.isEmpty() )
1800  {
1801  parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
1802  return QVariant();
1803  }
1804 
1805  if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
1806  {
1807  return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
1808  }
1809  else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
1810  {
1811  return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
1812  }
1813  else
1814  {
1815  parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
1816  return QVariant();
1817  }
1818 }
1819 
1820 static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
1821 {
1823  return floatToDegreeFormat( format, values, context, parent, node );
1824 }
1825 
1826 static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
1827 {
1829  return floatToDegreeFormat( format, values, context, parent, node );
1830 }
1831 
1832 static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1833 {
1834  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1835  QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
1836  qint64 seconds = d2.secsTo( d1 );
1837  return QVariant::fromValue( QgsInterval( seconds ) );
1838 }
1839 
1840 static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1841 {
1842  if ( !values.at( 0 ).canConvert<QDate>() )
1843  return QVariant();
1844 
1845  QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
1846  if ( !date.isValid() )
1847  return QVariant();
1848 
1849  // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
1850  // (to match PostgreSQL behavior)
1851  return date.dayOfWeek() % 7;
1852 }
1853 
1854 static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1855 {
1856  QVariant value = values.at( 0 );
1857  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
1858  if ( inter.isValid() )
1859  {
1860  return QVariant( inter.days() );
1861  }
1862  else
1863  {
1864  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
1865  return QVariant( d1.date().day() );
1866  }
1867 }
1868 
1869 static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1870 {
1871  QVariant value = values.at( 0 );
1872  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
1873  if ( inter.isValid() )
1874  {
1875  return QVariant( inter.years() );
1876  }
1877  else
1878  {
1879  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
1880  return QVariant( d1.date().year() );
1881  }
1882 }
1883 
1884 static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1885 {
1886  QVariant value = values.at( 0 );
1887  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
1888  if ( inter.isValid() )
1889  {
1890  return QVariant( inter.months() );
1891  }
1892  else
1893  {
1894  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
1895  return QVariant( d1.date().month() );
1896  }
1897 }
1898 
1899 static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1900 {
1901  QVariant value = values.at( 0 );
1902  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
1903  if ( inter.isValid() )
1904  {
1905  return QVariant( inter.weeks() );
1906  }
1907  else
1908  {
1909  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
1910  return QVariant( d1.date().weekNumber() );
1911  }
1912 }
1913 
1914 static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1915 {
1916  QVariant value = values.at( 0 );
1917  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
1918  if ( inter.isValid() )
1919  {
1920  return QVariant( inter.hours() );
1921  }
1922  else
1923  {
1924  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
1925  return QVariant( t1.hour() );
1926  }
1927 }
1928 
1929 static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1930 {
1931  QVariant value = values.at( 0 );
1932  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
1933  if ( inter.isValid() )
1934  {
1935  return QVariant( inter.minutes() );
1936  }
1937  else
1938  {
1939  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
1940  return QVariant( t1.minute() );
1941  }
1942 }
1943 
1944 static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1945 {
1946  QVariant value = values.at( 0 );
1947  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
1948  if ( inter.isValid() )
1949  {
1950  return QVariant( inter.seconds() );
1951  }
1952  else
1953  {
1954  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
1955  return QVariant( t1.second() );
1956  }
1957 }
1958 
1959 static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1960 {
1961  QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1962  if ( dt.isValid() )
1963  {
1964  return QVariant( dt.toMSecsSinceEpoch() );
1965  }
1966  else
1967  {
1968  return QVariant();
1969  }
1970 }
1971 
1972 #define ENSURE_GEOM_TYPE(f, g, geomtype) \
1973  if ( !(f).hasGeometry() ) \
1974  return QVariant(); \
1975  QgsGeometry g = (f).geometry(); \
1976  if ( (g).type() != (geomtype) ) \
1977  return QVariant();
1978 
1979 static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1980 {
1981  FEAT_FROM_CONTEXT( context, f )
1983  if ( g.isMultipart() )
1984  {
1985  return g.asMultiPoint().at( 0 ).x();
1986  }
1987  else
1988  {
1989  return g.asPoint().x();
1990  }
1991 }
1992 
1993 static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1994 {
1995  FEAT_FROM_CONTEXT( context, f )
1997  if ( g.isMultipart() )
1998  {
1999  return g.asMultiPoint().at( 0 ).y();
2000  }
2001  else
2002  {
2003  return g.asPoint().y();
2004  }
2005 }
2006 
2007 static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2008 {
2009  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2010  if ( geom.isNull() )
2011  return QVariant();
2012 
2013  //if single point, return the point's x coordinate
2014  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2015  {
2016  return geom.asPoint().x();
2017  }
2018 
2019  //otherwise return centroid x
2020  QgsGeometry centroid = geom.centroid();
2021  QVariant result( centroid.asPoint().x() );
2022  return result;
2023 }
2024 
2025 static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2026 {
2027  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2028  if ( geom.isNull() )
2029  return QVariant();
2030 
2031  //if single point, return the point's y coordinate
2032  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2033  {
2034  return geom.asPoint().y();
2035  }
2036 
2037  //otherwise return centroid y
2038  QgsGeometry centroid = geom.centroid();
2039  QVariant result( centroid.asPoint().y() );
2040  return result;
2041 }
2042 
2043 static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2044 {
2045  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2046  if ( geom.isNull() )
2047  return QVariant(); //or 0?
2048 
2049  //if single point, return the point's z coordinate
2050  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2051  {
2052  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2053  if ( point )
2054  return point->z();
2055  }
2056  else if ( geom.type() == QgsWkbTypes::PointGeometry && geom.isMultipart() )
2057  {
2058  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2059  {
2060  if ( collection->numGeometries() == 1 )
2061  {
2062  if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2063  return point->z();
2064  }
2065  }
2066  }
2067 
2068  return QVariant();
2069 }
2070 
2071 static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2072 {
2073  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2074  if ( geom.isNull() )
2075  return QVariant(); //or 0?
2076 
2077  //if single point, return the point's m value
2078  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2079  {
2080  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2081  if ( point )
2082  return point->m();
2083  }
2084  else if ( geom.type() == QgsWkbTypes::PointGeometry && geom.isMultipart() )
2085  {
2086  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2087  {
2088  if ( collection->numGeometries() == 1 )
2089  {
2090  if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2091  return point->m();
2092  }
2093  }
2094  }
2095 
2096  return QVariant();
2097 }
2098 
2099 static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2100 {
2101  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2102 
2103  if ( geom.isNull() )
2104  return QVariant();
2105 
2106  int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2107 
2108  if ( idx < 0 )
2109  {
2110  //negative idx
2111  int count = geom.constGet()->nCoordinates();
2112  idx = count + idx;
2113  }
2114  else
2115  {
2116  //positive idx is 1 based
2117  idx -= 1;
2118  }
2119 
2120  QgsVertexId vId;
2121  if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
2122  {
2123  parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
2124  return QVariant();
2125  }
2126 
2127  QgsPoint point = geom.constGet()->vertexAt( vId );
2128  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2129 }
2130 
2131 static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2132 {
2133  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2134 
2135  if ( geom.isNull() )
2136  return QVariant();
2137 
2138  QgsVertexId vId;
2139  if ( !geom.vertexIdFromVertexNr( 0, vId ) )
2140  {
2141  return QVariant();
2142  }
2143 
2144  QgsPoint point = geom.constGet()->vertexAt( vId );
2145  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2146 }
2147 
2148 static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2149 {
2150  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2151 
2152  if ( geom.isNull() )
2153  return QVariant();
2154 
2155  QgsVertexId vId;
2156  if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
2157  {
2158  return QVariant();
2159  }
2160 
2161  QgsPoint point = geom.constGet()->vertexAt( vId );
2162  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2163 }
2164 
2165 static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2166 {
2167  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2168 
2169  if ( geom.isNull() )
2170  return QVariant();
2171 
2172  bool ignoreClosing = false;
2173  if ( values.length() > 1 )
2174  {
2175  ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
2176  }
2177 
2178  QgsMultiPoint *mp = new QgsMultiPoint();
2179 
2180  const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
2181  for ( const QgsRingSequence &part : sequence )
2182  {
2183  for ( const QgsPointSequence &ring : part )
2184  {
2185  bool skipLast = false;
2186  if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
2187  {
2188  skipLast = true;
2189  }
2190 
2191  for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
2192  {
2193  mp->addGeometry( ring.at( i ).clone() );
2194  }
2195  }
2196  }
2197 
2198  return QVariant::fromValue( QgsGeometry( mp ) );
2199 }
2200 
2201 static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2202 {
2203  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2204 
2205  if ( geom.isNull() )
2206  return QVariant();
2207 
2208  const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
2209 
2210  //OK, now we have a complete list of segmentized lines from the geometry
2212  for ( QgsLineString *line : linesToProcess )
2213  {
2214  for ( int i = 0; i < line->numPoints() - 1; ++i )
2215  {
2216  QgsLineString *segment = new QgsLineString();
2217  segment->setPoints( QgsPointSequence()
2218  << line->pointN( i )
2219  << line->pointN( i + 1 ) );
2220  ml->addGeometry( segment );
2221  }
2222  delete line;
2223  }
2224 
2225  return QVariant::fromValue( QgsGeometry( ml ) );
2226 }
2227 
2228 static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2229 {
2230  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2231 
2232  if ( geom.isNull() )
2233  return QVariant();
2234 
2235  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
2236  if ( !curvePolygon && geom.isMultipart() )
2237  {
2238  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2239  {
2240  if ( collection->numGeometries() == 1 )
2241  {
2242  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
2243  }
2244  }
2245  }
2246 
2247  if ( !curvePolygon )
2248  return QVariant();
2249 
2250  //idx is 1 based
2251  qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
2252 
2253  if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
2254  return QVariant();
2255 
2256  QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
2257  QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
2258  return result;
2259 }
2260 
2261 static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2262 {
2263  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2264 
2265  if ( geom.isNull() )
2266  return QVariant();
2267 
2268  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
2269  if ( !collection )
2270  return QVariant();
2271 
2272  //idx is 1 based
2273  qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
2274 
2275  if ( idx < 0 || idx >= collection->numGeometries() )
2276  return QVariant();
2277 
2278  QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
2279  QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
2280  return result;
2281 }
2282 
2283 static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2284 {
2285  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2286 
2287  if ( geom.isNull() )
2288  return QVariant();
2289 
2290  QgsAbstractGeometry *boundary = geom.constGet()->boundary();
2291  if ( !boundary )
2292  return QVariant();
2293 
2294  return QVariant::fromValue( QgsGeometry( boundary ) );
2295 }
2296 
2297 static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2298 {
2299  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2300 
2301  if ( geom.isNull() )
2302  return QVariant();
2303 
2304  QgsGeometry merged = geom.mergeLines();
2305  if ( merged.isNull() )
2306  return QVariant();
2307 
2308  return QVariant::fromValue( merged );
2309 }
2310 
2311 static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2312 {
2313  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2314 
2315  if ( geom.isNull() )
2316  return QVariant();
2317 
2318  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2319 
2320  QgsGeometry simplified = geom.simplify( tolerance );
2321  if ( simplified.isNull() )
2322  return QVariant();
2323 
2324  return simplified;
2325 }
2326 
2327 static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2328 {
2329  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2330 
2331  if ( geom.isNull() )
2332  return QVariant();
2333 
2334  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2335 
2337 
2338  QgsGeometry simplified = simplifier.simplify( geom );
2339  if ( simplified.isNull() )
2340  return QVariant();
2341 
2342  return simplified;
2343 }
2344 
2345 static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2346 {
2347  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2348 
2349  if ( geom.isNull() )
2350  return QVariant();
2351 
2352  int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
2353  double offset = qBound( 0.0, QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.5 );
2354  double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
2355  double maxAngle = qBound( 0.0, QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 180.0 );
2356 
2357  QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
2358  if ( smoothed.isNull() )
2359  return QVariant();
2360 
2361  return smoothed;
2362 }
2363 
2364 static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2365 {
2366  QVariantList list;
2367  if ( values.size() == 1 && ( values.at( 0 ).type() == QVariant::List || values.at( 0 ).type() == QVariant::StringList ) )
2368  {
2369  list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
2370  }
2371  else
2372  {
2373  list = values;
2374  }
2375 
2376  QVector< QgsGeometry > parts;
2377  parts.reserve( list.size() );
2378  for ( const QVariant &value : qgis::as_const( list ) )
2379  {
2380  if ( value.canConvert<QgsGeometry>() )
2381  {
2382  parts << value.value<QgsGeometry>();
2383  }
2384  else
2385  {
2386  parent->setEvalErrorString( QStringLiteral( "Cannot convert to geometry" ) );
2387  return QgsGeometry();
2388  }
2389  }
2390 
2391  return QgsGeometry::collectGeometry( parts );
2392 }
2393 
2394 static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2395 {
2396  if ( values.count() < 2 || values.count() > 4 )
2397  {
2398  parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
2399  return QVariant();
2400  }
2401 
2402  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2403  double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2404  double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
2405  double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
2406  switch ( values.count() )
2407  {
2408  case 2:
2409  return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
2410  case 3:
2411  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointZ, x, y, z ) ) );
2412  case 4:
2413  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointZM, x, y, z, m ) ) );
2414  }
2415  return QVariant(); //avoid warning
2416 }
2417 
2418 static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2419 {
2420  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2421  double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2422  double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2423  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointM, x, y, 0.0, m ) ) );
2424 }
2425 
2426 static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2427 {
2428  if ( values.empty() )
2429  {
2430  return QVariant();
2431  }
2432 
2433  QVector<QgsPoint> points;
2434  points.reserve( values.count() );
2435 
2436  auto addPoint = [&points]( const QgsGeometry & geom )
2437  {
2438  if ( geom.isNull() )
2439  return;
2440 
2441  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
2442  return;
2443 
2444  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2445  if ( !point )
2446  return;
2447 
2448  points << *point;
2449  };
2450 
2451  for ( const QVariant &value : values )
2452  {
2453  if ( value.type() == QVariant::List )
2454  {
2455  const QVariantList list = value.toList();
2456  for ( const QVariant &v : list )
2457  {
2458  addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
2459  }
2460  }
2461  else
2462  {
2463  addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
2464  }
2465  }
2466 
2467  if ( points.count() < 2 )
2468  return QVariant();
2469 
2470  return QgsGeometry( new QgsLineString( points ) );
2471 }
2472 
2473 static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2474 {
2475  if ( values.count() < 1 )
2476  {
2477  parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
2478  return QVariant();
2479  }
2480 
2481  QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2482  if ( outerRing.type() != QgsWkbTypes::LineGeometry || outerRing.isNull() )
2483  return QVariant();
2484 
2485  std::unique_ptr< QgsPolygon > polygon = qgis::make_unique< QgsPolygon >();
2486 
2487  const QgsCurve *exteriorRing = qgsgeometry_cast< QgsCurve * >( outerRing.constGet() );
2488  if ( !exteriorRing && outerRing.isMultipart() )
2489  {
2490  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
2491  {
2492  if ( collection->numGeometries() == 1 )
2493  {
2494  exteriorRing = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
2495  }
2496  }
2497  }
2498 
2499  if ( !exteriorRing )
2500  return QVariant();
2501 
2502  polygon->setExteriorRing( exteriorRing->segmentize() );
2503 
2504 
2505  for ( int i = 1; i < values.count(); ++i )
2506  {
2507  QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
2508  if ( ringGeom.isNull() )
2509  continue;
2510 
2511  if ( ringGeom.type() != QgsWkbTypes::LineGeometry || ringGeom.isNull() )
2512  continue;
2513 
2514  const QgsCurve *ring = qgsgeometry_cast< QgsCurve * >( ringGeom.constGet() );
2515  if ( !ring && ringGeom.isMultipart() )
2516  {
2517  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
2518  {
2519  if ( collection->numGeometries() == 1 )
2520  {
2521  ring = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
2522  }
2523  }
2524  }
2525 
2526  if ( !ring )
2527  continue;
2528 
2529  polygon->addInteriorRing( ring->segmentize() );
2530  }
2531 
2532  return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
2533 }
2534 
2535 static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2536 {
2537  std::unique_ptr<QgsTriangle> tr( new QgsTriangle() );
2538  std::unique_ptr<QgsLineString> lineString( new QgsLineString() );
2539  lineString->clear();
2540 
2541  for ( const QVariant &value : values )
2542  {
2543  QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
2544  if ( geom.isNull() )
2545  return QVariant();
2546 
2547  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
2548  return QVariant();
2549 
2550  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2551  if ( !point && geom.isMultipart() )
2552  {
2553  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2554  {
2555  if ( collection->numGeometries() == 1 )
2556  {
2557  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
2558  }
2559  }
2560  }
2561 
2562  if ( !point )
2563  return QVariant();
2564 
2565  lineString->addVertex( *point );
2566  }
2567 
2568  tr->setExteriorRing( lineString.release() );
2569 
2570  return QVariant::fromValue( QgsGeometry( tr.release() ) );
2571 }
2572 
2573 static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2574 {
2575  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2576  if ( geom.isNull() )
2577  return QVariant();
2578 
2579  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
2580  return QVariant();
2581 
2582  double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2583  int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2584 
2585  if ( segment < 3 )
2586  {
2587  parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
2588  return QVariant();
2589  }
2590  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2591  if ( !point && geom.isMultipart() )
2592  {
2593  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2594  {
2595  if ( collection->numGeometries() == 1 )
2596  {
2597  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
2598  }
2599  }
2600  }
2601  if ( !point )
2602  return QVariant();
2603 
2604  QgsCircle circ( *point, radius );
2605  return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
2606 }
2607 
2608 static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2609 {
2610  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2611  if ( geom.isNull() )
2612  return QVariant();
2613 
2614  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
2615  return QVariant();
2616 
2617  double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2618  double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2619  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
2620  int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
2621  if ( segment < 3 )
2622  {
2623  parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
2624  return QVariant();
2625  }
2626  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2627  if ( !point && geom.isMultipart() )
2628  {
2629  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2630  {
2631  if ( collection->numGeometries() == 1 )
2632  {
2633  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
2634  }
2635  }
2636  }
2637  if ( !point )
2638  return QVariant();
2639 
2640  QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
2641  return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
2642 }
2643 
2644 static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2645 {
2646 
2647  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2648  if ( pt1.isNull() )
2649  return QVariant();
2650 
2651  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
2652  return QVariant();
2653 
2654  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
2655  if ( pt2.isNull() )
2656  return QVariant();
2657 
2658  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
2659  return QVariant();
2660 
2661  unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
2662  if ( nbEdges < 3 )
2663  {
2664  parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
2665  return QVariant();
2666  }
2667 
2668  QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
2670  {
2671  parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
2672  return QVariant();
2673  }
2674 
2675  const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
2676  if ( !center && pt1.isMultipart() )
2677  {
2678  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
2679  {
2680  if ( collection->numGeometries() == 1 )
2681  {
2682  center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
2683  }
2684  }
2685  }
2686  if ( !center )
2687  return QVariant();
2688 
2689  const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
2690  if ( !corner && pt2.isMultipart() )
2691  {
2692  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
2693  {
2694  if ( collection->numGeometries() == 1 )
2695  {
2696  corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
2697  }
2698  }
2699  }
2700  if ( !corner )
2701  return QVariant();
2702 
2703  QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
2704 
2705  return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
2706 
2707 }
2708 
2709 static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2710 {
2711  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2712  if ( pt1.isNull() )
2713  return QVariant();
2714  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
2715  return QVariant();
2716 
2717  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
2718  if ( pt2.isNull() )
2719  return QVariant();
2720  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
2721  return QVariant();
2722 
2723  const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
2724  const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
2725  QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
2726 
2727  return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
2728 }
2729 
2730 static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2731 {
2732  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2733  if ( pt1.isNull() )
2734  return QVariant();
2735  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
2736  return QVariant();
2737 
2738  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
2739  if ( pt2.isNull() )
2740  return QVariant();
2741  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
2742  return QVariant();
2743 
2744  QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
2745  if ( pt3.isNull() )
2746  return QVariant();
2747  if ( pt3.type() != QgsWkbTypes::PointGeometry || pt3.isMultipart() )
2748  return QVariant();
2749 
2750  QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
2751  if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
2752  {
2753  parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
2754  return QVariant();
2755  }
2756  const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
2757  const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
2758  const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
2759  QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
2760  return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
2761 }
2762 
2763 static QVariant pointAt( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) // helper function
2764 {
2765  FEAT_FROM_CONTEXT( context, f )
2766  int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
2767  QgsGeometry g = f.geometry();
2768  if ( g.isNull() )
2769  return QVariant();
2770 
2771  if ( idx < 0 )
2772  {
2773  idx += g.constGet()->nCoordinates();
2774  }
2775  if ( idx < 0 || idx >= g.constGet()->nCoordinates() )
2776  {
2777  parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
2778  return QVariant();
2779  }
2780 
2781  QgsPointXY p = g.vertexAt( idx );
2782  return QVariant( QPointF( p.x(), p.y() ) );
2783 }
2784 
2785 static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )
2786 {
2787  QVariant v = pointAt( values, f, parent );
2788  if ( v.type() == QVariant::PointF )
2789  return QVariant( v.toPointF().x() );
2790  else
2791  return QVariant();
2792 }
2793 static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )
2794 {
2795  QVariant v = pointAt( values, f, parent );
2796  if ( v.type() == QVariant::PointF )
2797  return QVariant( v.toPointF().y() );
2798  else
2799  return QVariant();
2800 }
2801 static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2802 {
2803  FEAT_FROM_CONTEXT( context, f )
2804  QgsGeometry geom = f.geometry();
2805  if ( !geom.isNull() )
2806  return QVariant::fromValue( geom );
2807  else
2808  return QVariant( QVariant::UserType );
2809 }
2810 static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2811 {
2812  QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2813  QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2814  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
2815  return result;
2816 }
2817 static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2818 {
2819  QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2821  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
2822  return result;
2823 }
2824 
2825 static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2826 {
2827  FEAT_FROM_CONTEXT( context, f )
2829  QgsDistanceArea *calc = parent->geomCalculator();
2830  if ( calc )
2831  {
2832  double area = calc->measureArea( f.geometry() );
2833  area = calc->convertAreaMeasurement( area, parent->areaUnits() );
2834  return QVariant( area );
2835  }
2836  else
2837  {
2838  return QVariant( f.geometry().area() );
2839  }
2840 }
2841 
2842 static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2843 {
2844  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2845 
2846  if ( geom.type() != QgsWkbTypes::PolygonGeometry )
2847  return QVariant();
2848 
2849  return QVariant( geom.area() );
2850 }
2851 
2852 static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2853 {
2854  FEAT_FROM_CONTEXT( context, f )
2856  QgsDistanceArea *calc = parent->geomCalculator();
2857  if ( calc )
2858  {
2859  double len = calc->measureLength( f.geometry() );
2860  len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
2861  return QVariant( len );
2862  }
2863  else
2864  {
2865  return QVariant( f.geometry().length() );
2866  }
2867 }
2868 
2869 static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2870 {
2871  FEAT_FROM_CONTEXT( context, f )
2873  QgsDistanceArea *calc = parent->geomCalculator();
2874  if ( calc )
2875  {
2876  double len = calc->measurePerimeter( f.geometry() );
2877  len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
2878  return QVariant( len );
2879  }
2880  else
2881  {
2882  return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
2883  }
2884 }
2885 
2886 static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2887 {
2888  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2889 
2890  if ( geom.type() != QgsWkbTypes::PolygonGeometry )
2891  return QVariant();
2892 
2893  //length for polygons = perimeter
2894  return QVariant( geom.length() );
2895 }
2896 
2897 static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2898 {
2899  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2900  return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
2901 }
2902 
2903 static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2904 {
2905  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2906  if ( geom.isNull() )
2907  return QVariant();
2908 
2909  return QVariant( geom.constGet()->partCount() );
2910 }
2911 
2912 static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2913 {
2914  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2915 
2916  if ( geom.isNull() )
2917  return QVariant();
2918 
2919  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
2920  if ( curvePolygon )
2921  return QVariant( curvePolygon->numInteriorRings() );
2922 
2923  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
2924  if ( collection )
2925  {
2926  //find first CurvePolygon in collection
2927  for ( int i = 0; i < collection->numGeometries(); ++i )
2928  {
2929  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
2930  if ( !curvePolygon )
2931  continue;
2932 
2933  return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
2934  }
2935  }
2936 
2937  return QVariant();
2938 }
2939 
2940 static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2941 {
2942  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2943 
2944  if ( geom.isNull() )
2945  return QVariant();
2946 
2947  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
2948  if ( curvePolygon )
2949  return QVariant( curvePolygon->ringCount() );
2950 
2951  bool foundPoly = false;
2952  int ringCount = 0;
2953  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
2954  if ( collection )
2955  {
2956  //find CurvePolygons in collection
2957  for ( int i = 0; i < collection->numGeometries(); ++i )
2958  {
2959  curvePolygon = qgsgeometry_cast< QgsCurvePolygon *>( collection->geometryN( i ) );
2960  if ( !curvePolygon )
2961  continue;
2962 
2963  foundPoly = true;
2964  ringCount += curvePolygon->ringCount();
2965  }
2966  }
2967 
2968  if ( !foundPoly )
2969  return QVariant();
2970 
2971  return QVariant( ringCount );
2972 }
2973 
2974 static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2975 {
2976  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2977  QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
2978  QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
2979  return result;
2980 }
2981 
2982 static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2983 {
2984  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2985  return QVariant::fromValue( geom.boundingBox().width() );
2986 }
2987 
2988 static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2989 {
2990  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2991  return QVariant::fromValue( geom.boundingBox().height() );
2992 }
2993 
2994 static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2995 {
2996  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2997  return QVariant::fromValue( geom.boundingBox().xMinimum() );
2998 }
2999 
3000 static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3001 {
3002  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3003  return QVariant::fromValue( geom.boundingBox().xMaximum() );
3004 }
3005 
3006 static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3007 {
3008  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3009  return QVariant::fromValue( geom.boundingBox().yMinimum() );
3010 }
3011 
3012 static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3013 {
3014  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3015  return QVariant::fromValue( geom.boundingBox().yMaximum() );
3016 }
3017 
3018 static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3019 {
3020  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3021  if ( geom.isNull() )
3022  return QVariant();
3023 
3024  std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
3025  flipped->swapXy();
3026  return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
3027 }
3028 
3029 static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3030 {
3031  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3032  if ( fGeom.isNull() )
3033  return QVariant();
3034 
3035  const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
3036  if ( !curve && fGeom.isMultipart() )
3037  {
3038  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
3039  {
3040  if ( collection->numGeometries() == 1 )
3041  {
3042  curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
3043  }
3044  }
3045  }
3046 
3047  if ( !curve )
3048  return QVariant();
3049 
3050  return QVariant::fromValue( curve->isClosed() );
3051 }
3052 
3053 static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3054 {
3055  if ( values.length() < 2 || values.length() > 3 )
3056  return QVariant();
3057 
3058  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3059  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3060 
3061  if ( fGeom.isNull() || sGeom.isNull() )
3062  return QVariant();
3063 
3064  std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
3065 
3066  if ( values.length() == 2 )
3067  {
3068  //two geometry arguments, return relation
3069  QString result = engine->relate( sGeom.constGet() );
3070  return QVariant::fromValue( result );
3071  }
3072  else
3073  {
3074  //three arguments, test pattern
3075  QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3076  bool result = engine->relatePattern( sGeom.constGet(), pattern );
3077  return QVariant::fromValue( result );
3078  }
3079 }
3080 
3081 static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3082 {
3083  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3084  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3085  return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
3086 }
3087 static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3088 {
3089  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3090  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3091  return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
3092 }
3093 static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3094 {
3095  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3096  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3097  return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
3098 }
3099 static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3100 {
3101  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3102  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3103  return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
3104 }
3105 static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3106 {
3107  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3108  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3109  return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
3110 }
3111 static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3112 {
3113  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3114  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3115  return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
3116 }
3117 static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3118 {
3119  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3120  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3121  return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
3122 }
3123 static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3124 {
3125  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3126  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3127  return fGeom.within( sGeom ) ? TVL_True : TVL_False;
3128 }
3129 static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3130 {
3131  if ( values.length() < 2 || values.length() > 3 )
3132  return QVariant();
3133 
3134  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3135  double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3136  int seg = 8;
3137  if ( values.length() == 3 )
3138  seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3139 
3140  QgsGeometry geom = fGeom.buffer( dist, seg );
3141  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3142  return result;
3143 }
3144 
3145 static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3146 {
3147  const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3148  const QgsGeometry reoriented = fGeom.forceRHR();
3149  return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
3150 }
3151 
3152 static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3153 {
3154  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3155  const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
3156  if ( !pt && fGeom.isMultipart() )
3157  {
3158  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
3159  {
3160  if ( collection->numGeometries() == 1 )
3161  {
3162  pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3163  }
3164  }
3165  }
3166 
3167  if ( !pt )
3168  {
3169  parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
3170  return QVariant();
3171  }
3172 
3173  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3174  double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3175  double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3176  double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3177 
3178  QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
3179  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3180  return result;
3181 }
3182 
3183 static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3184 {
3185  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3186  if ( fGeom.type() != QgsWkbTypes::LineGeometry )
3187  {
3188  parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
3189  return QVariant();
3190  }
3191 
3192  double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3193  double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3194  int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3195 
3196  QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
3197  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3198  return result;
3199 }
3200 
3201 static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3202 {
3203  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3204  if ( fGeom.type() != QgsWkbTypes::LineGeometry )
3205  {
3206  parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
3207  return QVariant();
3208  }
3209 
3210  int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
3211 
3212  QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
3213  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3214  return result;
3215 }
3216 
3217 static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3218 {
3219  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3220  double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3221  int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3222  QgsGeometry::JoinStyle join = static_cast< QgsGeometry::JoinStyle >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3223  if ( join < QgsGeometry::JoinStyleRound || join > QgsGeometry::JoinStyleBevel )
3224  return QVariant();
3225  double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3226 
3227  QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
3228  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3229  return result;
3230 }
3231 
3232 static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3233 {
3234  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3235  double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3236  int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3237  QgsGeometry::JoinStyle join = static_cast< QgsGeometry::JoinStyle >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3238  if ( join < QgsGeometry::JoinStyleRound || join > QgsGeometry::JoinStyleBevel )
3239  return QVariant();
3240  double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3241 
3242  QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, QgsGeometry::SideLeft, join, miterLimit );
3243  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3244  return result;
3245 }
3246 
3247 static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3248 {
3249  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3250  double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3251  double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3252 
3253  QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
3254  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3255  return result;
3256 }
3257 
3258 static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3259 {
3260  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3261  double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3262  double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3263  fGeom.translate( dx, dy );
3264  return QVariant::fromValue( fGeom );
3265 }
3266 static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3267 {
3268  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3269  QgsGeometry geom = fGeom.centroid();
3270  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3271  return result;
3272 }
3273 static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3274 {
3275  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3276  QgsGeometry geom = fGeom.pointOnSurface();
3277  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3278  return result;
3279 }
3280 
3281 static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3282 {
3283  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3284  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3285  QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
3286  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3287  return result;
3288 }
3289 
3290 static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3291 {
3292  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3293  QgsGeometry geom = fGeom.convexHull();
3294  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3295  return result;
3296 }
3297 
3298 
3299 static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3300 {
3301  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3302  int segments = 36;
3303  if ( values.length() == 2 )
3304  segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3305  if ( segments < 0 )
3306  {
3307  parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
3308  return QVariant();
3309  }
3310 
3311  QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
3312  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3313  return result;
3314 }
3315 
3316 static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3317 {
3318  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3319  QgsGeometry geom = fGeom.orientedMinimumBoundingBox( );
3320  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3321  return result;
3322 }
3323 
3324 static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3325 {
3326  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3327  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3328  QgsGeometry geom = fGeom.difference( sGeom );
3329  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3330  return result;
3331 }
3332 
3333 static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3334 {
3335  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3336  if ( fGeom.isNull() )
3337  return QVariant();
3338 
3339  QVariant result;
3340  if ( !fGeom.isMultipart() )
3341  {
3342  const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
3343  if ( !curve )
3344  return QVariant();
3345 
3346  QgsCurve *reversed = curve->reversed();
3347  result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
3348  }
3349  else
3350  {
3351  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
3352  std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
3353  for ( int i = 0; i < collection->numGeometries(); ++i )
3354  {
3355  if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
3356  {
3357  reversed->addGeometry( curve->reversed() );
3358  }
3359  else
3360  {
3361  reversed->addGeometry( collection->geometryN( i )->clone() );
3362  }
3363  }
3364  result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
3365  }
3366  return result;
3367 }
3368 
3369 static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3370 {
3371  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3372  if ( fGeom.isNull() )
3373  return QVariant();
3374 
3375  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
3376  if ( !curvePolygon && fGeom.isMultipart() )
3377  {
3378  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
3379  {
3380  if ( collection->numGeometries() == 1 )
3381  {
3382  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3383  }
3384  }
3385  }
3386 
3387  if ( !curvePolygon || !curvePolygon->exteriorRing() )
3388  return QVariant();
3389 
3390  QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
3391  QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
3392  return result;
3393 }
3394 
3395 static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3396 {
3397  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3398  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3399  return QVariant( fGeom.distance( sGeom ) );
3400 }
3401 
3402 static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3403 {
3404  QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3405  QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3406 
3407  double res = -1;
3408  if ( values.length() == 3 && values.at( 2 ).isValid() )
3409  {
3410  double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3411  densify = qBound( 0.0, densify, 1.0 );
3412  res = g1.hausdorffDistanceDensify( g2, densify );
3413  }
3414  else
3415  {
3416  res = g1.hausdorffDistance( g2 );
3417  }
3418 
3419  return res > -1 ? QVariant( res ) : QVariant();
3420 }
3421 
3422 static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3423 {
3424  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3425  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3426  QgsGeometry geom = fGeom.intersection( sGeom );
3427  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3428  return result;
3429 }
3430 static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3431 {
3432  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3433  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3434  QgsGeometry geom = fGeom.symDifference( sGeom );
3435  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3436  return result;
3437 }
3438 static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3439 {
3440  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3441  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3442  QgsGeometry geom = fGeom.combine( sGeom );
3443  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3444  return result;
3445 }
3446 static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3447 {
3448  if ( values.length() < 1 || values.length() > 2 )
3449  return QVariant();
3450 
3451  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3452  int prec = 8;
3453  if ( values.length() == 2 )
3454  prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3455  QString wkt = fGeom.asWkt( prec );
3456  return QVariant( wkt );
3457 }
3458 
3459 static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3460 {
3461  if ( values.length() != 2 )
3462  {
3463  parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %1 given." ).arg( values.length() ) );
3464  return QVariant();
3465  }
3466 
3467  QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3468  QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3469 
3470  const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
3471  if ( !pt1 && fGeom1.isMultipart() )
3472  {
3473  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
3474  {
3475  if ( collection->numGeometries() == 1 )
3476  {
3477  pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3478  }
3479  }
3480  }
3481 
3482  const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
3483  if ( !pt2 && fGeom2.isMultipart() )
3484  {
3485  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
3486  {
3487  if ( collection->numGeometries() == 1 )
3488  {
3489  pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3490  }
3491  }
3492  }
3493 
3494  if ( !pt1 || !pt2 )
3495  {
3496  parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
3497  return QVariant();
3498  }
3499 
3500  // Code from PostGIS
3501  if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
3502  {
3503  if ( pt1->y() < pt2->y() )
3504  return 0.0;
3505  else if ( pt1->y() > pt2->y() )
3506  return M_PI;
3507  else
3508  return 0;
3509  }
3510 
3511  if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
3512  {
3513  if ( pt1->x() < pt2->x() )
3514  return M_PI_2;
3515  else if ( pt1->x() > pt2->x() )
3516  return M_PI + ( M_PI_2 );
3517  else
3518  return 0;
3519  }
3520 
3521  if ( pt1->x() < pt2->x() )
3522  {
3523  if ( pt1->y() < pt2->y() )
3524  {
3525  return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
3526  }
3527  else /* ( pt1->y() > pt2->y() ) - equality case handled above */
3528  {
3529  return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
3530  + ( M_PI_2 );
3531  }
3532  }
3533 
3534  else /* ( pt1->x() > pt2->x() ) - equality case handled above */
3535  {
3536  if ( pt1->y() > pt2->y() )
3537  {
3538  return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
3539  + M_PI;
3540  }
3541  else /* ( pt1->y() < pt2->y() ) - equality case handled above */
3542  {
3543  return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
3544  + ( M_PI + ( M_PI_2 ) );
3545  }
3546  }
3547 }
3548 
3549 static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3550 {
3551  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3552 
3553  if ( geom.type() != QgsWkbTypes::PointGeometry )
3554  {
3555  parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
3556  return QVariant();
3557  }
3558 
3559  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3560  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3561  double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3562 
3563  const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet() );
3564  QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
3565 
3566  return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
3567 }
3568 
3569 static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3570 {
3571  QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3572  QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3573 
3574  const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
3575  if ( !pt1 && fGeom1.isMultipart() )
3576  {
3577  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
3578  {
3579  if ( collection->numGeometries() == 1 )
3580  {
3581  pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3582  }
3583  }
3584  }
3585  const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
3586  if ( !pt2 && fGeom2.isMultipart() )
3587  {
3588  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
3589  {
3590  if ( collection->numGeometries() == 1 )
3591  {
3592  pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3593  }
3594  }
3595  }
3596 
3597  if ( ( fGeom1.type() != QgsWkbTypes::PointGeometry ) || ( fGeom2.type() != QgsWkbTypes::PointGeometry ) ||
3598  !pt1 || !pt2 )
3599  {
3600  parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
3601  return QVariant();
3602  }
3603 
3604  return pt1->inclination( *pt2 );
3605 
3606 }
3607 
3608 static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3609 {
3610  if ( values.length() != 3 )
3611  return QVariant();
3612 
3613  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3614  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3615  double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3616 
3617  QgsGeometry geom = fGeom.extrude( x, y );
3618 
3619  QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
3620  return result;
3621 }
3622 
3623 static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
3624 {
3625  if ( values.length() < 2 )
3626  return QVariant();
3627 
3628  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3629 
3630  if ( !fGeom.isMultipart() )
3631  return values.at( 0 );
3632 
3633  QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3634  QVariant cachedExpression;
3635  if ( ctx )
3636  cachedExpression = ctx->cachedValue( expString );
3637  QgsExpression expression;
3638 
3639  if ( cachedExpression.isValid() )
3640  {
3641  expression = cachedExpression.value<QgsExpression>();
3642  }
3643  else
3644  expression = QgsExpression( expString );
3645 
3646  bool asc = values.value( 2 ).toBool();
3647 
3648  QgsExpressionContext *unconstedContext = nullptr;
3649  QgsFeature f;
3650  if ( ctx )
3651  {
3652  // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
3653  // so no reason to worry
3654  unconstedContext = const_cast<QgsExpressionContext *>( ctx );
3655  f = ctx->feature();
3656  }
3657  else
3658  {
3659  // If there's no context provided, create a fake one
3660  unconstedContext = new QgsExpressionContext();
3661  }
3662 
3663  const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
3664  Q_ASSERT( collection ); // Should have failed the multipart check above
3665 
3667  orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
3668  QgsExpressionSorter sorter( orderBy );
3669 
3670  QList<QgsFeature> partFeatures;
3671  partFeatures.reserve( collection->partCount() );
3672  for ( int i = 0; i < collection->partCount(); ++i )
3673  {
3674  f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
3675  partFeatures << f;
3676  }
3677 
3678  sorter.sortFeatures( partFeatures, unconstedContext );
3679 
3681 
3682  Q_ASSERT( orderedGeom );
3683 
3684  while ( orderedGeom->partCount() )
3685  orderedGeom->removeGeometry( 0 );
3686 
3687  for ( const QgsFeature &feature : qgis::as_const( partFeatures ) )
3688  {
3689  orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
3690  }
3691 
3692  QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
3693 
3694  if ( !ctx )
3695  delete unconstedContext;
3696 
3697  return result;
3698 }
3699 
3700 static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3701 {
3702  QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3703  QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3704 
3705  QgsGeometry geom = fromGeom.nearestPoint( toGeom );
3706 
3707  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3708  return result;
3709 }
3710 
3711 static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3712 {
3713  QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3714  QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3715 
3716  QgsGeometry geom = fromGeom.shortestLine( toGeom );
3717 
3718  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3719  return result;
3720 }
3721 
3722 static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3723 {
3724  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3725  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3726 
3727  QgsGeometry geom = lineGeom.interpolate( distance );
3728 
3729  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3730  return result;
3731 }
3732 
3733 static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3734 {
3735  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3736  if ( lineGeom.type() != QgsWkbTypes::LineGeometry )
3737  {
3738  parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
3739  return QVariant();
3740  }
3741 
3742  const QgsCurve *curve = nullptr;
3743  if ( !lineGeom.isMultipart() )
3744  curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
3745  else
3746  {
3747  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
3748  {
3749  if ( collection->numGeometries() > 0 )
3750  {
3751  curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
3752  }
3753  }
3754  }
3755  if ( !curve )
3756  return QVariant();
3757 
3758  double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3759  double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3760 
3761  std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
3762  QgsGeometry result( std::move( substring ) );
3763  return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
3764 }
3765 
3766 static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3767 {
3768  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3769  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3770 
3771  return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
3772 }
3773 
3774 static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3775 {
3776  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3777  int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3778  if ( vertex < 0 )
3779  {
3780  //negative idx
3781  int count = geom.constGet()->nCoordinates();
3782  vertex = count + vertex;
3783  }
3784 
3785  return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
3786 }
3787 
3788 static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3789 {
3790  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3791  int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3792  if ( vertex < 0 )
3793  {
3794  //negative idx
3795  int count = geom.constGet()->nCoordinates();
3796  vertex = count + vertex;
3797  }
3798 
3799  return geom.distanceToVertex( vertex );
3800 }
3801 
3802 static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3803 {
3804  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3805  QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3806 
3807  double distance = lineGeom.lineLocatePoint( pointGeom );
3808 
3809  return distance >= 0 ? distance : QVariant();
3810 }
3811 
3812 static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3813 {
3814  if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
3815  {
3816  double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3817  return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
3818  }
3819 
3820  if ( values.length() >= 1 )
3821  {
3822  double number = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
3823  return QVariant( qlonglong( std::round( number ) ) );
3824  }
3825 
3826  return QVariant();
3827 }
3828 
3829 static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3830 {
3831  Q_UNUSED( values )
3832  Q_UNUSED( parent )
3833  return M_PI;
3834 }
3835 
3836 static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3837 {
3838  double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3839  int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3840  if ( places < 0 )
3841  {
3842  parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
3843  return QVariant();
3844  }
3845  QLocale locale = QLocale();
3846  locale.setNumberOptions( locale.numberOptions() &= ~QLocale::NumberOption::OmitGroupSeparator );
3847  return locale.toString( value, 'f', places );
3848 }
3849 
3850 static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3851 {
3852  QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3853  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3854  return dt.toString( format );
3855 }
3856 
3857 static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
3858 {
3859  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
3860  int avg = ( color.red() + color.green() + color.blue() ) / 3;
3861  int alpha = color.alpha();
3862 
3863  color.setRgb( avg, avg, avg, alpha );
3864 
3865  return QgsSymbolLayerUtils::encodeColor( color );
3866 }
3867 
3868 static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3869 {
3870  QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
3871  QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
3872  double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3873  if ( ratio > 1 )
3874  {
3875  ratio = 1;
3876  }
3877  else if ( ratio < 0 )
3878  {
3879  ratio = 0;
3880  }
3881 
3882  int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
3883  int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
3884  int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
3885  int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
3886 
3887  QColor newColor( red, green, blue, alpha );
3888 
3889  return QgsSymbolLayerUtils::encodeColor( newColor );
3890 }
3891 
3892 static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3893 {
3894  int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
3895  int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3896  int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3897  QColor color = QColor( red, green, blue );
3898  if ( ! color.isValid() )
3899  {
3900  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
3901  color = QColor( 0, 0, 0 );
3902  }
3903 
3904  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
3905 }
3906 
3907 static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3908 {
3909  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
3910  QVariant value = node->eval( parent, context );
3911  if ( parent->hasEvalError() )
3912  {
3913  parent->setEvalErrorString( QString() );
3914  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
3916  value = node->eval( parent, context );
3918  }
3919  return value;
3920 }
3921 
3922 static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3923 {
3924  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
3926  QVariant value = node->eval( parent, context );
3928  if ( value.toBool() )
3929  {
3930  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
3932  value = node->eval( parent, context );
3934  }
3935  else
3936  {
3937  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
3939  value = node->eval( parent, context );
3941  }
3942  return value;
3943 }
3944 
3945 static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3946 {
3947  int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
3948  int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3949  int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3950  int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
3951  QColor color = QColor( red, green, blue, alpha );
3952  if ( ! color.isValid() )
3953  {
3954  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
3955  color = QColor( 0, 0, 0 );
3956  }
3957  return QgsSymbolLayerUtils::encodeColor( color );
3958 }
3959 
3960 QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3961 {
3962  QgsGradientColorRamp expRamp;
3963  const QgsColorRamp *ramp = nullptr;
3964  if ( values.at( 0 ).canConvert<QgsGradientColorRamp>() )
3965  {
3966  expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
3967  ramp = &expRamp;
3968  }
3969  else
3970  {
3971  QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3972  ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
3973  if ( ! ramp )
3974  {
3975  parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
3976  return QVariant();
3977  }
3978  }
3979 
3980  double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3981  QColor color = ramp->color( value );
3982  return QgsSymbolLayerUtils::encodeColor( color );
3983 }
3984 
3985 static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3986 {
3987  // Hue ranges from 0 - 360
3988  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
3989  // Saturation ranges from 0 - 100
3990  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
3991  // Lightness ranges from 0 - 100
3992  double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
3993 
3994  QColor color = QColor::fromHslF( hue, saturation, lightness );
3995 
3996  if ( ! color.isValid() )
3997  {
3998  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
3999  color = QColor( 0, 0, 0 );
4000  }
4001 
4002  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
4003 }
4004 
4005 static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4006 {
4007  // Hue ranges from 0 - 360
4008  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
4009  // Saturation ranges from 0 - 100
4010  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4011  // Lightness ranges from 0 - 100
4012  double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4013  // Alpha ranges from 0 - 255
4014  double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
4015 
4016  QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
4017  if ( ! color.isValid() )
4018  {
4019  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
4020  color = QColor( 0, 0, 0 );
4021  }
4022  return QgsSymbolLayerUtils::encodeColor( color );
4023 }
4024 
4025 static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4026 {
4027  // Hue ranges from 0 - 360
4028  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
4029  // Saturation ranges from 0 - 100
4030  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4031  // Value ranges from 0 - 100
4032  double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4033 
4034  QColor color = QColor::fromHsvF( hue, saturation, value );
4035 
4036  if ( ! color.isValid() )
4037  {
4038  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
4039  color = QColor( 0, 0, 0 );
4040  }
4041 
4042  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
4043 }
4044 
4045 static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4046 {
4047  // Hue ranges from 0 - 360
4048  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
4049  // Saturation ranges from 0 - 100
4050  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4051  // Value ranges from 0 - 100
4052  double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4053  // Alpha ranges from 0 - 255
4054  double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
4055 
4056  QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
4057  if ( ! color.isValid() )
4058  {
4059  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
4060  color = QColor( 0, 0, 0 );
4061  }
4062  return QgsSymbolLayerUtils::encodeColor( color );
4063 }
4064 
4065 static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4066 {
4067  // Cyan ranges from 0 - 100
4068  double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
4069  // Magenta ranges from 0 - 100
4070  double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4071  // Yellow ranges from 0 - 100
4072  double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4073  // Black ranges from 0 - 100
4074  double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
4075 
4076  QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
4077 
4078  if ( ! color.isValid() )
4079  {
4080  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
4081  color = QColor( 0, 0, 0 );
4082  }
4083 
4084  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
4085 }
4086 
4087 static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4088 {
4089  // Cyan ranges from 0 - 100
4090  double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
4091  // Magenta ranges from 0 - 100
4092  double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4093  // Yellow ranges from 0 - 100
4094  double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4095  // Black ranges from 0 - 100
4096  double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
4097  // Alpha ranges from 0 - 255
4098  double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
4099 
4100  QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
4101  if ( ! color.isValid() )
4102  {
4103  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
4104  color = QColor( 0, 0, 0 );
4105  }
4106  return QgsSymbolLayerUtils::encodeColor( color );
4107 }
4108 
4109 static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4110 {
4111  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4112  if ( ! color.isValid() )
4113  {
4114  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
4115  return QVariant();
4116  }
4117 
4118  QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4119  if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
4120  return color.red();
4121  else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
4122  return color.green();
4123  else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
4124  return color.blue();
4125  else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
4126  return color.alpha();
4127  else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
4128  return color.hsvHueF() * 360;
4129  else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
4130  return color.hsvSaturationF() * 100;
4131  else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
4132  return color.valueF() * 100;
4133  else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
4134  return color.hslHueF() * 360;
4135  else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
4136  return color.hslSaturationF() * 100;
4137  else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
4138  return color.lightnessF() * 100;
4139  else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
4140  return color.cyanF() * 100;
4141  else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
4142  return color.magentaF() * 100;
4143  else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
4144  return color.yellowF() * 100;
4145  else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
4146  return color.blackF() * 100;
4147 
4148  parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
4149  return QVariant();
4150 }
4151 
4152 static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4153 {
4154  const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
4155  if ( map.count() < 1 )
4156  {
4157  parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
4158  return QVariant();
4159  }
4160 
4161  QList< QColor > colors;
4162  QgsGradientStopsList stops;
4163  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
4164  {
4165  colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
4166  if ( !colors.last().isValid() )
4167  {
4168  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
4169  return QVariant();
4170  }
4171 
4172  double step = it.key().toDouble();
4173  if ( it == map.constBegin() )
4174  {
4175  if ( step != 0.0 )
4176  stops << QgsGradientStop( step, colors.last() );
4177  }
4178  else if ( it == map.constEnd() )
4179  {
4180  if ( step != 1.0 )
4181  stops << QgsGradientStop( step, colors.last() );
4182  }
4183  else
4184  {
4185  stops << QgsGradientStop( step, colors.last() );
4186  }
4187  }
4188  bool discrete = values.at( 1 ).toBool();
4189 
4190  return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
4191 }
4192 
4193 static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4194 {
4195  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4196  if ( ! color.isValid() )
4197  {
4198  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
4199  return QVariant();
4200  }
4201 
4202  QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4203  int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4204  if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
4205  color.setRed( value );
4206  else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
4207  color.setGreen( value );
4208  else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
4209  color.setBlue( value );
4210  else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
4211  color.setAlpha( value );
4212  else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
4213  color.setHsv( value, color.hsvSaturation(), color.value(), color.alpha() );
4214  else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
4215  color.setHsvF( color.hsvHueF(), value / 100.0, color.valueF(), color.alphaF() );
4216  else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
4217  color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), value / 100.0, color.alphaF() );
4218  else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
4219  color.setHsl( value, color.hslSaturation(), color.lightness(), color.alpha() );
4220  else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
4221  color.setHslF( color.hslHueF(), value / 100.0, color.lightnessF(), color.alphaF() );
4222  else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
4223  color.setHslF( color.hslHueF(), color.hslSaturationF(), value / 100.0, color.alphaF() );
4224  else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
4225  color.setCmykF( value / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
4226  else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
4227  color.setCmykF( color.cyanF(), value / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
4228  else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
4229  color.setCmykF( color.cyanF(), color.magentaF(), value / 100.0, color.blackF(), color.alphaF() );
4230  else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
4231  color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), value / 100.0, color.alphaF() );
4232  else
4233  {
4234  parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
4235  return QVariant();
4236  }
4237  return QgsSymbolLayerUtils::encodeColor( color );
4238 }
4239 
4240 static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4241 {
4242  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4243  if ( ! color.isValid() )
4244  {
4245  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
4246  return QVariant();
4247  }
4248 
4249  color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
4250 
4251  return QgsSymbolLayerUtils::encodeColor( color );
4252 }
4253 
4254 static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4255 {
4256  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4257  if ( ! color.isValid() )
4258  {
4259  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
4260  return QVariant();
4261  }
4262 
4263  color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
4264 
4265  return QgsSymbolLayerUtils::encodeColor( color );
4266 }
4267 
4268 static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4269 {
4270  QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
4271  QgsGeometry geom = feat.geometry();
4272  if ( !geom.isNull() )
4273  return QVariant::fromValue( geom );
4274  return QVariant();
4275 }
4276 
4277 static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4278 {
4279  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4280  QString sAuthId = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4281  QString dAuthId = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4282 
4284  if ( ! s.isValid() )
4285  return QVariant::fromValue( fGeom );
4287  if ( ! d.isValid() )
4288  return QVariant::fromValue( fGeom );
4289 
4291  if ( context )
4292  tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
4293  QgsCoordinateTransform t( s, d, tContext );
4294  try
4295  {
4296  if ( fGeom.transform( t ) == 0 )
4297  return QVariant::fromValue( fGeom );
4298  }
4299  catch ( QgsCsException &cse )
4300  {
4301  QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
4302  return QVariant();
4303  }
4304  return QVariant();
4305 }
4306 
4307 
4308 static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4309 {
4310  QVariant result;
4311  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
4312  if ( vl )
4313  {
4314  QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
4315 
4316  QgsFeatureRequest req;
4317  req.setFilterFid( fid );
4318  req.setTimeout( 10000 );
4319  req.setRequestMayBeNested( true );
4320  QgsFeatureIterator fIt = vl->getFeatures( req );
4321 
4322  QgsFeature fet;
4323  if ( fIt.nextFeature( fet ) )
4324  result = QVariant::fromValue( fet );
4325  }
4326 
4327  return result;
4328 }
4329 
4330 static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4331 {
4332  //arguments: 1. layer id / name, 2. key attribute, 3. eq value
4333 
4334  std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), parent );
4335 
4336  //no layer found
4337  if ( !featureSource )
4338  {
4339  return QVariant();
4340  }
4341 
4342  QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4343  int attributeId = featureSource->fields().lookupField( attribute );
4344  if ( attributeId == -1 )
4345  {
4346  return QVariant();
4347  }
4348 
4349  const QVariant &attVal = values.at( 2 );
4350 
4351  const QString cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
4352  if ( context && context->hasCachedValue( cacheValueKey ) )
4353  {
4354  return context->cachedValue( cacheValueKey );
4355  }
4356 
4357  QgsFeatureRequest req;
4358  req.setFilterExpression( QStringLiteral( "%1=%2" ).arg( QgsExpression::quotedColumnRef( attribute ),
4359  QgsExpression::quotedString( attVal.toString() ) ) );
4360  req.setLimit( 1 );
4361  req.setTimeout( 10000 );
4362  req.setRequestMayBeNested( true );
4363  if ( !parent->needsGeometry() )
4364  {
4366  }
4367  QgsFeatureIterator fIt = featureSource->getFeatures( req );
4368 
4369  QgsFeature fet;
4370  QVariant res;
4371  if ( fIt.nextFeature( fet ) )
4372  {
4373  res = QVariant::fromValue( fet );
4374  }
4375 
4376  if ( context )
4377  context->setCachedValue( cacheValueKey, res );
4378  return res;
4379 }
4380 
4381 static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4382 {
4383  QVariant result;
4384  QString fieldName;
4385 
4386  if ( context )
4387  {
4388  if ( !values.isEmpty() )
4389  {
4390  QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
4391  if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
4392  fieldName = col->name();
4393  else if ( values.size() == 2 )
4394  fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4395  }
4396 
4397  QVariant value = values.at( 0 );
4398 
4399  const QgsFields fields = context->fields();
4400  int fieldIndex = fields.lookupField( fieldName );
4401 
4402  if ( fieldIndex == -1 )
4403  {
4404  parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
4405  }
4406  else
4407  {
4408  QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
4409 
4410  const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
4411  if ( context->hasCachedValue( cacheValueKey ) )
4412  {
4413  return context->cachedValue( cacheValueKey );
4414  }
4415 
4416  const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
4418 
4419  const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
4420 
4421  QVariant cache;
4422  if ( !context->hasCachedValue( cacheKey ) )
4423  {
4424  cache = formatter->createCache( layer, fieldIndex, setup.config() );
4425  context->setCachedValue( cacheKey, cache );
4426  }
4427  else
4428  cache = context->cachedValue( cacheKey );
4429 
4430  result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
4431 
4432  context->setCachedValue( cacheValueKey, result );
4433  }
4434  }
4435  else
4436  {
4437  parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
4438  }
4439 
4440  return result;
4441 }
4442 
4443 static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4444 {
4445  QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
4446 
4447  if ( !layer )
4448  return QVariant();
4449 
4450  // here, we always prefer the layer metadata values over the older server-specific published values
4451  QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4452  if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
4453  return layer->name();
4454  else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
4455  return layer->id();
4456  else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
4457  return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->title();
4458  else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
4459  return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->abstract();
4460  else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
4461  {
4462  QStringList keywords;
4463  const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
4464  for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
4465  {
4466  keywords.append( it.value() );
4467  }
4468  if ( !keywords.isEmpty() )
4469  return keywords;
4470  return layer->keywordList();
4471  }
4472  else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
4473  return layer->dataUrl();
4474  else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
4475  {
4476  return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->attribution() );
4477  }
4478  else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
4479  return layer->attributionUrl();
4480  else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
4481  return layer->publicSource();
4482  else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
4483  return layer->minimumScale();
4484  else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
4485  return layer->maximumScale();
4486  else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
4487  return layer->crs().authid();
4488  else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
4489  return layer->crs().toProj4();
4490  else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
4491  return layer->crs().description();
4492  else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
4493  {
4494  QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
4495  QVariant result = QVariant::fromValue( extentGeom );
4496  return result;
4497  }
4498  else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
4499  {
4500  switch ( layer->type() )
4501  {
4503  return QCoreApplication::translate( "expressions", "Vector" );
4505  return QCoreApplication::translate( "expressions", "Raster" );
4507  return QCoreApplication::translate( "expressions", "Mesh" );
4509  return QCoreApplication::translate( "expressions", "Plugin" );
4510  }
4511  }
4512  else
4513  {
4514  //vector layer methods
4515  QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
4516  if ( vLayer )
4517  {
4518  if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
4519  return vLayer->storageType();
4520  else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
4522  else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
4523  return QVariant::fromValue( vLayer->featureCount() );
4524  else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
4525  {
4526  if ( vLayer->dataProvider() )
4527  {
4528  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
4529  return decodedUri.value( QStringLiteral( "path" ) );
4530  }
4531  }
4532  }
4533  }
4534 
4535  return QVariant();
4536 }
4537 
4538 static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4539 {
4540  QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
4541  if ( !layer )
4542  {
4543  parent->setEvalErrorString( QObject::tr( "Cannot find layer %1" ).arg( values.at( 0 ).toString() ) );
4544  return QVariant();
4545  }
4546 
4547  if ( !layer->dataProvider() )
4548  {
4549  parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
4550  return QVariant();
4551  }
4552 
4553  const QString uriPart = values.at( 1 ).toString();
4554 
4555  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
4556 
4557  if ( !uriPart.isNull() )
4558  {
4559  return decodedUri.value( values.at( 1 ).toString() );
4560  }
4561  else
4562  {
4563  return decodedUri;
4564  }
4565 }
4566 
4567 static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4568 {
4569  QString layerIdOrName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4570 
4571  //try to find a matching layer by name
4572  QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerIdOrName ); //search by id first
4573  if ( !layer )
4574  {
4575  QList<QgsMapLayer *> layersByName = QgsProject::instance()->mapLayersByName( layerIdOrName );
4576  if ( !layersByName.isEmpty() )
4577  {
4578  layer = layersByName.at( 0 );
4579  }
4580  }
4581 
4582  if ( !layer )
4583  return QVariant();
4584 
4585  QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
4586  if ( !rl )
4587  return QVariant();
4588 
4589  int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4590  if ( band < 1 || band > rl->bandCount() )
4591  {
4592  parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer %2" ).arg( band ).arg( layerIdOrName ) );
4593  return QVariant();
4594  }
4595 
4596  QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4597  int stat = 0;
4598 
4599  if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
4600  stat = QgsRasterBandStats::Mean;
4601  else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
4603  else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
4604  stat = QgsRasterBandStats::Min;
4605  else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
4606  stat = QgsRasterBandStats::Max;
4607  else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
4609  else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
4610  stat = QgsRasterBandStats::Sum;
4611  else
4612  {
4613  parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
4614  return QVariant();
4615  }
4616 
4617  QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
4618  switch ( stat )
4619  {
4621  return stats.mean;
4623  return stats.stdDev;
4625  return stats.minimumValue;
4627  return stats.maximumValue;
4629  return stats.range;
4631  return stats.sum;
4632  }
4633  return QVariant();
4634 }
4635 
4636 static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
4637 {
4638  return values;
4639 }
4640 
4641 static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4642 {
4643  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4644  bool ascending = values.value( 1 ).toBool();
4645  std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
4646  return list;
4647 }
4648 
4649 static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4650 {
4651  return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
4652 }
4653 
4654 static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4655 {
4656  return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
4657 }
4658 
4659 static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4660 {
4661  QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4662  QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
4663  int match = 0;
4664  for ( const auto &item : listB )
4665  {
4666  if ( listA.contains( item ) )
4667  match++;
4668  }
4669 
4670  return QVariant( match == listB.count() );
4671 }
4672 
4673 static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4674 {
4675  return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
4676 }
4677 
4678 static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4679 {
4680  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4681  const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4682  if ( pos < 0 || pos >= list.length() ) return QVariant();
4683  return list.at( pos );
4684 }
4685 
4686 static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4687 {
4688  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4689  return list.value( 0 );
4690 }
4691 
4692 static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4693 {
4694  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4695  return list.value( list.size() - 1 );
4696 }
4697 
4698 static QVariant convertToSameType( const QVariant &value, QVariant::Type type )
4699 {
4700  QVariant result = value;
4701  result.convert( static_cast<int>( type ) );
4702  return result;
4703 }
4704 
4705 static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4706 {
4707  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4708  list.append( values.at( 1 ) );
4709  return convertToSameType( list, values.at( 0 ).type() );
4710 }
4711 
4712 static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4713 {
4714  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4715  list.prepend( values.at( 1 ) );
4716  return convertToSameType( list, values.at( 0 ).type() );
4717 }
4718 
4719 static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4720 {
4721  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4722  list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
4723  return convertToSameType( list, values.at( 0 ).type() );
4724 }
4725 
4726 static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4727 {
4728  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4729  list.removeAt( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
4730  return convertToSameType( list, values.at( 0 ).type() );
4731 }
4732 
4733 static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4734 {
4735  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4736  list.removeAll( values.at( 1 ) );
4737  return convertToSameType( list, values.at( 0 ).type() );
4738 }
4739 
4740 static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4741 {
4742  QVariantList list;
4743  for ( const QVariant &cur : values )
4744  {
4745  list += QgsExpressionUtils::getListValue( cur, parent );
4746  }
4747  return convertToSameType( list, values.at( 0 ).type() );
4748 }
4749 
4750 static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4751 {
4752  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4753  int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4754  const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4755  int slice_length = 0;
4756  // negative positions means positions taken relative to the end of the array
4757  if ( start_pos < 0 )
4758  {
4759  start_pos = list.length() + start_pos;
4760  }
4761  if ( end_pos >= 0 )
4762  {
4763  slice_length = end_pos - start_pos + 1;
4764  }
4765  else
4766  {
4767  slice_length = list.length() + end_pos - start_pos + 1;
4768  }
4769  //avoid negative lengths in QList.mid function
4770  if ( slice_length < 0 )
4771  {
4772  slice_length = 0;
4773  }
4774  list = list.mid( start_pos, slice_length );
4775  return list;
4776 }
4777 
4778 static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4779 {
4780  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4781  std::reverse( list.begin(), list.end() );
4782  return list;
4783 }
4784 
4785 static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4786 {
4787  const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4788  const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
4789  for ( const QVariant &cur : array2 )
4790  {
4791  if ( array1.contains( cur ) )
4792  return QVariant( true );
4793  }
4794  return QVariant( false );
4795 }
4796 
4797 static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4798 {
4799  QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4800 
4801  QVariantList distinct;
4802 
4803  for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
4804  {
4805  if ( !distinct.contains( *it ) )
4806  {
4807  distinct += ( *it );
4808  }
4809  }
4810 
4811  return distinct;
4812 }
4813 
4814 static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4815 {
4816  QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4817  QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4818  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4819 
4820  QString str;
4821 
4822  for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
4823  {
4824  str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
4825  if ( it != ( array.constEnd() - 1 ) )
4826  {
4827  str += delimiter;
4828  }
4829  }
4830 
4831  return QVariant( str );
4832 }
4833 
4834 static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4835 {
4836  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4837  QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4838  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4839 
4840  QStringList list = str.split( delimiter );
4841  QVariantList array;
4842 
4843  for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
4844  {
4845  array += ( !( *it ).isEmpty() ) ? *it : empty;
4846  }
4847 
4848  return array;
4849 }
4850 
4851 static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4852 {
4853  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4854  QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
4855  if ( document.isNull() )
4856  return QVariant();
4857 
4858  return document.toVariant();
4859 }
4860 
4861 static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4862 {
4863  Q_UNUSED( parent )
4864  QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
4865  return document.toJson( QJsonDocument::Compact );
4866 }
4867 
4868 static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4869 {
4870  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4871  if ( str.isEmpty() )
4872  return QVariantMap();
4873  str = str.trimmed();
4874 
4875  return QgsHstoreUtils::parse( str );
4876 }
4877 
4878 static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4879 {
4880  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
4881  return QgsHstoreUtils::build( map );
4882 }
4883 
4884 static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4885 {
4886  QVariantMap result;
4887  for ( int i = 0; i + 1 < values.length(); i += 2 )
4888  {
4889  result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
4890  }
4891  return result;
4892 }
4893 
4894 static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4895 {
4896  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
4897 }
4898 
4899 static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4900 {
4901  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
4902 }
4903 
4904 static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4905 {
4906  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
4907  map.remove( values.at( 1 ).toString() );
4908  return map;
4909 }
4910 
4911 static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4912 {
4913  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
4914  map.insert( values.at( 1 ).toString(), values.at( 2 ) );
4915  return map;
4916 }
4917 
4918 static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4919 {
4920  QVariantMap result;
4921  for ( const QVariant &cur : values )
4922  {
4923  const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
4924  for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
4925  result.insert( it.key(), it.value() );
4926  }
4927  return result;
4928 }
4929 
4930 static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4931 {
4932  return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
4933 }
4934 
4935 static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4936 {
4937  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
4938 }
4939 
4940 static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
4941 {
4942  QString envVarName = values.at( 0 ).toString();
4943  return QProcessEnvironment::systemEnvironment().value( envVarName );
4944 }
4945 
4946 static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4947 {
4948  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4949  return QFileInfo( file ).completeBaseName();
4950 }
4951 
4952 static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4953 {
4954  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4955  return QFileInfo( file ).completeSuffix();
4956 }
4957 
4958 static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4959 {
4960  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4961  return QFileInfo::exists( file );
4962 }
4963 
4964 static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4965 {
4966  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4967  return QFileInfo( file ).fileName();
4968 }
4969 
4970 static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4971 {
4972  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4973  return QFileInfo( file ).isFile();
4974 }
4975 
4976 static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4977 {
4978  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4979  return QFileInfo( file ).isDir();
4980 }
4981 
4982 static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4983 {
4984  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4985  return QDir::toNativeSeparators( QFileInfo( file ).path() );
4986 }
4987 
4988 static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4989 {
4990  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4991  return QFileInfo( file ).size();
4992 }
4993 
4994 
4995 
4996 
4997 const QList<QgsExpressionFunction *> &QgsExpression::Functions()
4998 {
4999  // The construction of the list isn't thread-safe, and without the mutex,
5000  // crashes in the WFS provider may occur, since it can parse expressions
5001  // in parallel.
5002  // The mutex needs to be recursive.
5003  static QMutex sFunctionsMutex( QMutex::Recursive );
5004  QMutexLocker locker( &sFunctionsMutex );
5005 
5006  if ( sFunctions.isEmpty() )
5007  {
5009  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
5010  << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
5011  << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
5012 
5013  QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
5014  aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
5015  << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
5016 
5017  QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
5018  aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
5019 
5020  sFunctions
5021  << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
5022  << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
5023  << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
5024  << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringList() << QStringLiteral( "Math" ) << QStringLiteral( "GeometryGroup" ) )
5025  << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringList() << QStringLiteral( "Math" ) << QStringLiteral( "GeometryGroup" ) )
5026  << new QgsStaticExpressionFunction( QStringLiteral( "project" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "elevation" ), true, M_PI_2 ), fcnProject, QStringLiteral( "GeometryGroup" ) )
5027  << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
5028  << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
5029  << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
5030  << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
5031  << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
5032  << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
5033  << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
5034  << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
5035  << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
5036  << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
5037  << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
5038  << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
5039  << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
5040 
5041  QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnRnd, QStringLiteral( "Math" ) );
5042  randFunc->setIsStatic( false );
5043  sFunctions << randFunc;
5044 
5045  QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction( QStringLiteral( "randf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ), true, 0.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ), true, 1.0 ), fcnRndF, QStringLiteral( "Math" ) );
5046  randfFunc->setIsStatic( false );
5047  sFunctions << randfFunc;
5048 
5049  sFunctions
5050  << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
5051  << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
5052  << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
5053  << new QgsStaticExpressionFunction( QStringLiteral( "scale_linear" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "val" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ), fcnLinearScale, QStringLiteral( "Math" ) )
5054  << new QgsStaticExpressionFunction( QStringLiteral( "scale_exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "val" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "exponent" ) ), fcnExpScale, QStringLiteral( "Math" ) )
5055  << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
5056  << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
5057  << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
5058  << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
5059  << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
5060  << new QgsStaticExpressionFunction( QStringLiteral( "to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToString, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tostring" ) )
5061  << new QgsStaticExpressionFunction( QStringLiteral( "to_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ), fcnToDateTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todatetime" ) )
5062  << new QgsStaticExpressionFunction( QStringLiteral( "to_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ), fcnToDate, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todate" ) )
5063  << new QgsStaticExpressionFunction( QStringLiteral( "to_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ), fcnToTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "totime" ) )
5064  << new QgsStaticExpressionFunction( QStringLiteral( "to_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInterval, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tointerval" ) )
5065  << new QgsStaticExpressionFunction( QStringLiteral( "to_dm" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinute, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todm" ) )
5066  << new QgsStaticExpressionFunction( QStringLiteral( "to_dms" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinuteSecond, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todms" ) )
5067  << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
5068  << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
5069  << new QgsStaticExpressionFunction( QStringLiteral( "if" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "condition" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_true" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_false" ) ), fcnIf, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
5070  << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
5071 
5072  << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
5074  << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
5075  << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
5076  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
5077  << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
5078  << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
5079  << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
5080  fcnAggregate,
5081  QStringLiteral( "Aggregates" ),
5082  QString(),
5083  []( const QgsExpressionNodeFunction * node )
5084  {
5085  // usesGeometry callback: return true if @parent variable is referenced
5086 
5087  if ( !node )
5088  return true;
5089 
5090  if ( !node->args() )
5091  return false;
5092 
5093  QSet<QString> referencedVars;
5094  if ( node->args()->count() > 2 )
5095  {
5096  QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
5097  referencedVars = subExpressionNode->referencedVariables();
5098  }
5099 
5100  if ( node->args()->count() > 3 )
5101  {
5102  QgsExpressionNode *filterNode = node->args()->at( 3 );
5103  referencedVars.unite( filterNode->referencedVariables() );
5104  }
5105  return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
5106  },
5107  []( const QgsExpressionNodeFunction * node )
5108  {
5109  // referencedColumns callback: return AllAttributes if @parent variable is referenced
5110 
5111  if ( !node )
5112  return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
5113 
5114  if ( !node->args() )
5115  return QSet<QString>();
5116 
5117  QSet<QString> referencedCols;
5118  QSet<QString> referencedVars;
5119 
5120  if ( node->args()->count() > 2 )
5121  {
5122  QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
5123  referencedVars = subExpressionNode->referencedVariables();
5124  referencedCols = subExpressionNode->referencedColumns();
5125  }
5126  if ( node->args()->count() > 3 )
5127  {
5128  QgsExpressionNode *filterNode = node->args()->at( 3 );
5129  referencedVars = filterNode->referencedVariables();
5130  referencedCols.unite( filterNode->referencedColumns() );
5131  }
5132 
5133  if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
5134  return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
5135  else
5136  return referencedCols;
5137  },
5138  true
5139  )
5140 
5141  << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
5142  << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
5143  << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
5144  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
5145  << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
5146  << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
5147  fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
5148 
5149  << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5150  << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5151  << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5152  << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5153  << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5154  << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5155  << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5156  << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5157  << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5158  << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5159  << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5160  << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5161  << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5162  << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5163  << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5164  << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5165  << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5166  << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5167  << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5168  << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5169  << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
5170 
5171  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
5172  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
5173 
5174  << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
5175  << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
5176  << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
5177  fcnAge, QStringLiteral( "Date and Time" ) )
5178  << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
5179  << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
5180  << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
5181  << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
5182  << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
5183  << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
5184  << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
5185  << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
5186  << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
5187  << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
5188  << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
5189  << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
5190  << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
5191  << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
5192  << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
5193  << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
5194  << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
5195  << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
5196  << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
5197  << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
5198  << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
5199  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
5200  << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
5201  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
5202  << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start " ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
5203  false, QSet< QString >(), false, QStringList(), true )
5204  << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
5205  << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
5206  << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
5207  << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
5208  << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
5209  << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
5210  << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
5211  << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ) ), fcnFormatNumber, QStringLiteral( "String" ) )
5212  << new QgsStaticExpressionFunction( QStringLiteral( "format_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ) ), fcnFormatDate, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "Date and Time" ) )
5213  << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
5214  << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
5215  << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
5216  << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
5217  fcnColorMixRgb, QStringLiteral( "Color" ) )
5218  << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
5219  << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
5220  << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
5221  fcnColorRgb, QStringLiteral( "Color" ) )
5222  << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
5223  << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
5224  << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
5225  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
5226  fncColorRgba, QStringLiteral( "Color" ) )
5227  << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
5228  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
5229  fcnRampColor, QStringLiteral( "Color" ) )
5230  << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
5231  << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
5232  fcnCreateRamp, QStringLiteral( "Color" ) )
5233  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
5234  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
5235  << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
5236  fcnColorHsl, QStringLiteral( "Color" ) )
5237  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
5238  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
5239  << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
5240  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
5241  fncColorHsla, QStringLiteral( "Color" ) )
5242  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
5243  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
5244  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
5245  fcnColorHsv, QStringLiteral( "Color" ) )
5246  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
5247  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
5248  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
5249  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
5250  fncColorHsva, QStringLiteral( "Color" ) )
5251  << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
5252  << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
5253  << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
5254  << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
5255  fcnColorCmyk, QStringLiteral( "Color" ) )
5256  << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
5257  << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
5258  << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
5259  << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
5260  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
5261  fncColorCmyka, QStringLiteral( "Color" ) )
5262  << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
5263  << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
5264  fncColorPart, QStringLiteral( "Color" ) )
5265  << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
5266  << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
5267  fncDarker, QStringLiteral( "Color" ) )
5268  << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
5269  << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
5270  fncLighter, QStringLiteral( "Color" ) )
5271  << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
5272 
5273  // file info
5274  << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
5275  fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
5276  << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
5277  fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
5278  << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
5279  fcnFileExists, QStringLiteral( "Files and Paths" ) )
5280  << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
5281  fcnFileName, QStringLiteral( "Files and Paths" ) )
5282  << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
5283  fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
5284  << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
5285  fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
5286  << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
5287  fcnFilePath, QStringLiteral( "Files and Paths" ) )
5288  << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
5289  fcnFileSize, QStringLiteral( "Files and Paths" ) )
5290 
5291  // deprecated stuff - hidden from users
5292  << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
5293 
5294  QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
5295  geomFunc->setIsStatic( false );
5296  sFunctions << geomFunc;
5297 
5298  QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
5299  areaFunc->setIsStatic( false );
5300  sFunctions << areaFunc;
5301 
5302  sFunctions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
5303 
5304  QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
5305  lengthFunc->setIsStatic( false );
5306  sFunctions << lengthFunc;
5307 
5308  QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
5309  perimeterFunc->setIsStatic( false );
5310  sFunctions << perimeterFunc;
5311 
5312  sFunctions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
5313 
5314  QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
5315  xFunc->setIsStatic( false );
5316  sFunctions << xFunc;
5317 
5318  QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
5319  yFunc->setIsStatic( false );
5320  sFunctions << yFunc;
5321 
5322  sFunctions
5323  << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
5324  << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
5325  << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
5326  << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
5327  << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
5328  << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
5329  << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
5330  << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), -1, fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
5331  << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
5332  << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
5333  << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
5334  << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
5335  << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
5336  << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
5337  fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
5338  << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
5339  << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
5340  << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5341  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5342  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
5343  fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
5344  << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
5345  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5346  << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
5347  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
5348  fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
5349  << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
5350  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5351  << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
5352  << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
5353  << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
5354  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
5355  fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
5356  << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
5357  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5358  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5359  << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
5360  << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
5361  fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
5362  << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
5363  << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
5364  << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
5365  fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
5366  << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
5367  << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
5368  << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
5369  << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
5370  << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
5371  fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) );
5372  QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "i" ) ), fcnXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) << QStringLiteral( "x_at" ) );
5373  xAtFunc->setIsStatic( false );
5374  sFunctions << xAtFunc;
5375 
5376  QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "i" ) ), fcnYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) << QStringLiteral( "y_at" ) );
5377  yAtFunc->setIsStatic( false );
5378  sFunctions << yAtFunc;
5379 
5380  sFunctions
5381  << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
5382  << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
5383  << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
5384  << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
5385  << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
5386  << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
5387  << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
5388  << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
5389  << new QgsStaticExpressionFunction( QStringLiteral( "intersects_bbox" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBbox, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "bbox" ) )
5390  << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry a" ) )
5391  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry b" ) ),
5392  fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
5393  << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry a" ) )
5394  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry b" ) ),
5395  fcnIntersects, QStringLiteral( "GeometryGroup" ) )
5396  << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry a" ) )
5397  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry b" ) ),
5398  fcnTouches, QStringLiteral( "GeometryGroup" ) )
5399  << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry a" ) )
5400  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry b" ) ),
5401  fcnCrosses, QStringLiteral( "GeometryGroup" ) )
5402  << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry a" ) )
5403  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry b" ) ),
5404  fcnContains, QStringLiteral( "GeometryGroup" ) )
5405  << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry a" ) )
5406  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry b" ) ),
5407  fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
5408  << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry a" ) )
5409  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry b" ) ),
5410  fcnWithin, QStringLiteral( "GeometryGroup" ) )
5411  << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) )
5412  << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
5413  << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
5414  fcnTranslate, QStringLiteral( "GeometryGroup" ) )
5415  << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), -1, fcnBuffer, QStringLiteral( "GeometryGroup" ) )
5416  << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
5417  fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
5418  << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
5419  << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
5420  << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
5421  << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
5422  << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
5423  << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5424  << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
5425  << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
5426  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
5427  , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
5428  << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5429  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
5430  , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
5431  << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5432  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
5433  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
5434  << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QgsGeometry::JoinStyleRound )
5435  << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
5436  fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
5437  << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5438  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
5439  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
5440  << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QgsGeometry::JoinStyleRound )
5441  << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
5442  fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
5443  << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5444  << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
5445  << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
5446  fcnExtend, QStringLiteral( "GeometryGroup" ) )
5447  << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
5448  << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
5449  << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5450  << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
5451  << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnReverse, QStringLiteral( "GeometryGroup" ) )
5452  << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
5453  << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5454  << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
5455  fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
5456  << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5457  << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
5458  fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
5459  << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
5460  << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
5461  << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
5462  << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
5463  << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
5464  << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
5465  << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
5466  << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
5467  << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
5468  << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
5469  << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
5470  << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
5471  << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
5472  << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
5473  << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
5474  << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
5475  << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
5476  << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
5477  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
5478  fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
5479  << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
5480  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5481  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
5482  fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
5483  << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry_a" ) )
5484  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry_b" ) ),
5485  fcnDifference, QStringLiteral( "GeometryGroup" ) )
5486  << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry a" ) )
5487  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry b" ) ),
5488  fcnDistance, QStringLiteral( "GeometryGroup" ) )
5489  << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
5490  << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
5491  fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
5492  << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry_a" ) )
5493  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry_b" ) ),
5494  fcnIntersection, QStringLiteral( "GeometryGroup" ) )
5495  << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
5496  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
5497  fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
5498  << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
5499  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
5500  fcnCombine, QStringLiteral( "GeometryGroup" ) )
5501  << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
5502  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
5503  fcnCombine, QStringLiteral( "GeometryGroup" ) )
5504  << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), -1, fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
5505  << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
5506  << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) )
5507  << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
5508  << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
5509  fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
5510  << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) )
5511  << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
5512  << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
5513  fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() );
5514 
5515  QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) )
5516  << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
5517  << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ) ),
5518  fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
5519 
5520  orderPartsFunc->setIsStaticFunction(
5521  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
5522  {
5523  const QList< QgsExpressionNode *> argList = node->args()->list();
5524  for ( QgsExpressionNode *argNode : argList )
5525  {
5526  if ( !argNode->isStatic( parent, context ) )
5527  return false;
5528  }
5529 
5530  if ( node->args()->count() > 1 )
5531  {
5532  QgsExpressionNode *argNode = node->args()->at( 1 );
5533 
5534  QString expString = argNode->eval( parent, context ).toString();
5535 
5536  QgsExpression e( expString );
5537 
5538  if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
5539  return true;
5540  }
5541 
5542  return true;
5543  } );
5544 
5545  orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
5546  {
5547  if ( node->args()->count() > 1 )
5548  {
5549  QgsExpressionNode *argNode = node->args()->at( 1 );
5550  QString expression = argNode->eval( parent, context ).toString();
5551  QgsExpression e( expression );
5552  e.prepare( context );
5553  context->setCachedValue( expression, QVariant::fromValue( e ) );
5554  }
5555  return true;
5556  }
5557  );
5558  sFunctions << orderPartsFunc;
5559 
5560  sFunctions
5561  << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
5562  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
5563  fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
5564  << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
5565  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
5566  fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
5567  << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5568  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
5569  << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5570  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
5571  << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5572  << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
5573  << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5574  << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
5575  << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5576  << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
5577  << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
5578  << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
5579 
5580 
5581  // **Record** functions
5582 
5583  QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
5584  idFunc->setIsStatic( false );
5585  sFunctions << idFunc;
5586 
5587  QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
5588  currentFeatureFunc->setIsStatic( false );
5589  sFunctions << currentFeatureFunc;
5590 
5591  QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction( QStringLiteral( "uuid" ), 0, fcnUuid, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$uuid" ) );
5592  uuidFunc->setIsStatic( false );
5593  sFunctions << uuidFunc;
5594 
5595  sFunctions
5596  << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
5597  << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
5598  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
5599  fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
5600  << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
5601  << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
5602  fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false )
5603  << new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
5604  fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
5605 
5606  QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction(
5607  QStringLiteral( "is_selected" ),
5608  -1,
5609  fcnIsSelected,
5610  QStringLiteral( "Record and Attributes" ),
5611  QString(),
5612  false,
5613  QSet<QString>()
5614  );
5615  isSelectedFunc->setIsStatic( false );
5616  sFunctions << isSelectedFunc;
5617 
5618  sFunctions
5619  << new QgsStaticExpressionFunction(
5620  QStringLiteral( "num_selected" ),
5621  -1,
5622  fcnNumSelected,
5623  QStringLiteral( "Record and Attributes" ),
5624  QString(),
5625  false,
5626  QSet<QString>()
5627  );
5628 
5629  sFunctions
5630  << new QgsStaticExpressionFunction(
5631  QStringLiteral( "sqlite_fetch_and_increment" ),
5633  << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
5634  << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
5635  << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
5636  << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
5637  << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
5638  << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
5639  fcnSqliteFetchAndIncrement,
5640  QStringLiteral( "Record and Attributes" )
5641  );
5642 
5643  // **Fields and Values** functions
5644  QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
5645 
5646  representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
5647  {
5648  Q_UNUSED( context )
5649  if ( node->args()->count() == 1 )
5650  {
5651  QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
5652  if ( colRef )
5653  {
5654  return true;
5655  }
5656  else
5657  {
5658  parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
5659  return false;
5660  }
5661  }
5662  else if ( node->args()->count() == 2 )
5663  {
5664  return true;
5665  }
5666  else
5667  {
5668  parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
5669  return false;
5670  }
5671  }
5672  );
5673 
5674  sFunctions << representValueFunc;
5675 
5676  // **General** functions
5677  sFunctions
5678  << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
5679  << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
5680  fcnGetLayerProperty, QStringLiteral( "General" ) )
5681  << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
5683  << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
5684  << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
5685  fcnDecodeUri, QStringLiteral( "Map Layers" ) )
5686  << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
5687  << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
5688  << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
5689 
5690  // **var** function
5691  QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
5692  varFunction->setIsStaticFunction(
5693  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
5694  {
5695  /* A variable node is static if it has a static name and the name can be found at prepare
5696  * time and is tagged with isStatic.
5697  * It is not static if a variable is set during iteration or not tagged isStatic.
5698  * (e.g. geom_part variable)
5699  */
5700  if ( node->args()->count() > 0 )
5701  {
5702  QgsExpressionNode *argNode = node->args()->at( 0 );
5703 
5704  if ( !argNode->isStatic( parent, context ) )
5705  return false;
5706 
5707  QString varName = argNode->eval( parent, context ).toString();
5708 
5709  const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
5710  return scope ? scope->isStatic( varName ) : false;
5711  }
5712  return false;
5713  }
5714  );
5715 
5716  sFunctions
5717  << varFunction;
5718  QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
5719  evalFunc->setIsStaticFunction(
5720  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
5721  {
5722  if ( node->args()->count() > 0 )
5723  {
5724  QgsExpressionNode *argNode = node->args()->at( 0 );
5725 
5726  if ( argNode->isStatic( parent, context ) )
5727  {
5728  QString expString = argNode->eval( parent, context ).toString();
5729 
5730  QgsExpression e( expString );
5731 
5732  if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
5733  return true;
5734  }
5735  }
5736 
5737  return false;
5738  } );
5739 
5740  sFunctions << evalFunc;
5741 
5742  QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
5743  attributeFunc->setIsStaticFunction(
5744  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
5745  {
5746  const QList< QgsExpressionNode *> argList = node->args()->list();
5747  for ( QgsExpressionNode *argNode : argList )
5748  {
5749  if ( !argNode->isStatic( parent, context ) )
5750  return false;
5751  }
5752 
5753  if ( node->args()->count() == 1 )
5754  {
5755  // not static -- this is the variant which uses the current feature taken direct from the expression context
5756  return false;
5757  }
5758 
5759  return true;
5760  } );
5761  sFunctions << attributeFunc;
5762 
5763  sFunctions
5764  << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
5766  << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
5767 
5768  // functions for arrays
5771  << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
5772  << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
5773  << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
5774  << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
5775  << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
5776  << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
5777  << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
5778  << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
5779  << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
5780  << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
5781  << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
5782  << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
5783  << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
5784  << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ) )
5785  << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
5786  << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
5787  << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
5788  << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
5789  << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
5790  << new QgsStaticExpressionFunction( QStringLiteral( "array_to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnArrayToString, QStringLiteral( "Arrays" ) )
5791  << new QgsStaticExpressionFunction( QStringLiteral( "string_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnStringToArray, QStringLiteral( "Arrays" ) )
5792  << new QgsStaticExpressionFunction( QStringLiteral( "generate_series" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "stop" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "step" ), true, 1.0 ), fcnGenerateSeries, QStringLiteral( "Arrays" ) )
5793 
5794  //functions for maps
5795  << new QgsStaticExpressionFunction( QStringLiteral( "json_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLoadJson, QStringLiteral( "Maps" ) )
5796  << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ) )
5797  << new QgsStaticExpressionFunction( QStringLiteral( "map_to_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnWriteJson, QStringLiteral( "Maps" ) )
5798  << new QgsStaticExpressionFunction( QStringLiteral( "to_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "json_string" ) ), fcnWriteJson, QStringLiteral( "Maps" ) )
5799  << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
5800  << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
5801  << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
5802  << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
5803  << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
5804  << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
5805  << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
5806  << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
5807  << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
5808  << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
5809  ;
5810 
5812 
5813  //QgsExpression has ownership of all built-in functions
5814  for ( QgsExpressionFunction *func : qgis::as_const( sFunctions ) )
5815  {
5816  sOwnedFunctions << func;
5817  sBuiltinFunctions << func->name();
5818  sBuiltinFunctions.append( func->aliases() );
5819  }
5820  }
5821  return sFunctions;
5822 }
5823 
5825  : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList()
5826  << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
5827  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
5828  QStringLiteral( "Arrays" ) )
5829 {
5830 
5831 }
5832 
5834 {
5835  bool isStatic = false;
5836 
5837  QgsExpressionNode::NodeList *args = node->args();
5838 
5839  if ( args->count() < 2 )
5840  return false;
5841 
5842  if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
5843  {
5844  isStatic = true;
5845  }
5846  return isStatic;
5847 }
5848 
5850 {
5851  Q_UNUSED( node )
5852  QVariantList result;
5853 
5854  if ( args->count() < 2 )
5855  // error
5856  return result;
5857 
5858  QVariantList array = args->at( 0 )->eval( parent, context ).toList();
5859 
5860  QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
5861  std::unique_ptr< QgsExpressionContext > tempContext;
5862  if ( !subContext )
5863  {
5864  tempContext = qgis::make_unique< QgsExpressionContext >();
5865  subContext = tempContext.get();
5866  }
5867 
5869  subContext->appendScope( subScope );
5870 
5871  for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
5872  {
5873  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
5874  result << args->at( 1 )->eval( parent, subContext );
5875  }
5876 
5877  if ( context )
5878  delete subContext->popScope();
5879 
5880  return result;
5881 }
5882 
5883 QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
5884 {
5885  // This is a dummy function, all the real handling is in run
5886  Q_UNUSED( values )
5887  Q_UNUSED( context )
5888  Q_UNUSED( parent )
5889  Q_UNUSED( node )
5890 
5891  Q_ASSERT( false );
5892  return QVariant();
5893 }
5894 
5896 {
5897  QgsExpressionNode::NodeList *args = node->args();
5898 
5899  if ( args->count() < 2 )
5900  // error
5901  return false;
5902 
5903  args->at( 0 )->prepare( parent, context );
5904 
5905  QgsExpressionContext subContext;
5906  if ( context )
5907  subContext = *context;
5908 
5910  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
5911  subContext.appendScope( subScope );
5912 
5913  args->at( 1 )->prepare( parent, &subContext );
5914 
5915  return true;
5916 }
5917 
5919  : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
5920  << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
5921  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
5922  QStringLiteral( "Arrays" ) )
5923 {
5924 
5925 }
5926 
5928 {
5929  bool isStatic = false;
5930 
5931  QgsExpressionNode::NodeList *args = node->args();
5932 
5933  if ( args->count() < 2 )
5934  return false;
5935 
5936  if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
5937  {
5938  isStatic = true;
5939  }
5940  return isStatic;
5941 }
5942 
5944 {
5945  Q_UNUSED( node )
5946  QVariantList result;
5947 
5948  if ( args->count() < 2 )
5949  // error
5950  return result;
5951 
5952  const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
5953 
5954  QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
5955  std::unique_ptr< QgsExpressionContext > tempContext;
5956  if ( !subContext )
5957  {
5958  tempContext = qgis::make_unique< QgsExpressionContext >();
5959  subContext = tempContext.get();
5960  }
5961 
5963  subContext->appendScope( subScope );
5964 
5965  for ( const QVariant &value : array )
5966  {
5967  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
5968  if ( args->at( 1 )->eval( parent, subContext ).toBool() )
5969  result << value;
5970  }
5971 
5972  if ( context )
5973  delete subContext->popScope();
5974 
5975  return result;
5976 }
5977 
5978 QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
5979 {
5980  // This is a dummy function, all the real handling is in run
5981  Q_UNUSED( values )
5982  Q_UNUSED( context )
5983  Q_UNUSED( parent )
5984  Q_UNUSED( node )
5985 
5986  Q_ASSERT( false );
5987  return QVariant();
5988 }
5989 
5991 {
5992  QgsExpressionNode::NodeList *args = node->args();
5993 
5994  if ( args->count() < 2 )
5995  // error
5996  return false;
5997 
5998  args->at( 0 )->prepare( parent, context );
5999 
6000  QgsExpressionContext subContext;
6001  if ( context )
6002  subContext = *context;
6003 
6005  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
6006  subContext.appendScope( subScope );
6007 
6008  args->at( 1 )->prepare( parent, &subContext );
6009 
6010  return true;
6011 }
6013  : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
6014  QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
6015  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
6016  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
6017  QStringLiteral( "General" ) )
6018 {
6019 
6020 }
6021 
6023 {
6024  bool isStatic = false;
6025 
6026  QgsExpressionNode::NodeList *args = node->args();
6027 
6028  if ( args->count() < 3 )
6029  return false;
6030 
6031  // We only need to check if the node evaluation is static, if both - name and value - are static.
6032  if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
6033  {
6034  QVariant name = args->at( 0 )->eval( parent, context );
6035  QVariant value = args->at( 1 )->eval( parent, context );
6036 
6037  // Temporarily append a new scope to provide the variable
6038  appendTemporaryVariable( context, name.toString(), value );
6039  if ( args->at( 2 )->isStatic( parent, context ) )
6040  isStatic = true;
6041  popTemporaryVariable( context );
6042  }
6043 
6044  return isStatic;
6045 }
6046 
6048 {
6049  Q_UNUSED( node )
6050  QVariant result;
6051 
6052  if ( args->count() < 3 )
6053  // error
6054  return result;
6055 
6056  QVariant name = args->at( 0 )->eval( parent, context );
6057  QVariant value = args->at( 1 )->eval( parent, context );
6058 
6059  const QgsExpressionContext *updatedContext = context;
6060  std::unique_ptr< QgsExpressionContext > tempContext;
6061  if ( !updatedContext )
6062  {
6063  tempContext = qgis::make_unique< QgsExpressionContext >();
6064  updatedContext = tempContext.get();
6065  }
6066 
6067  appendTemporaryVariable( updatedContext, name.toString(), value );
6068  result = args->at( 2 )->eval( parent, updatedContext );
6069 
6070  if ( context )
6071  popTemporaryVariable( updatedContext );
6072 
6073  return result;
6074 }
6075 
6076 QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6077 {
6078  // This is a dummy function, all the real handling is in run
6079  Q_UNUSED( values )
6080  Q_UNUSED( context )
6081  Q_UNUSED( parent )
6082  Q_UNUSED( node )
6083 
6084  Q_ASSERT( false );
6085  return QVariant();
6086 }
6087 
6089 {
6090  QgsExpressionNode::NodeList *args = node->args();
6091 
6092  if ( args->count() < 3 )
6093  // error
6094  return false;
6095 
6096  QVariant name = args->at( 0 )->prepare( parent, context );
6097  QVariant value = args->at( 1 )->prepare( parent, context );
6098 
6099  const QgsExpressionContext *updatedContext = context;
6100  std::unique_ptr< QgsExpressionContext > tempContext;
6101  if ( !updatedContext )
6102  {
6103  tempContext = qgis::make_unique< QgsExpressionContext >();
6104  updatedContext = tempContext.get();
6105  }
6106 
6107  appendTemporaryVariable( updatedContext, name.toString(), value );
6108  args->at( 2 )->prepare( parent, updatedContext );
6109 
6110  if ( context )
6111  popTemporaryVariable( updatedContext );
6112 
6113  return true;
6114 }
6115 
6116 void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
6117 {
6118  QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
6119  delete updatedContext->popScope();
6120 }
6121 
6122 void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
6123 {
6125  scope->addVariable( QgsExpressionContextScope::StaticVariable( name, value, true ) );
6126 
6127  QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
6128  updatedContext->appendScope( scope );
6129 }
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function&#39;s...
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:387
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:183
Class for parsing and evaluation of expressions (formerly called "search strings").
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2)
Construct a QgsQuadrilateral as a square from a diagonal.
QgsFeatureId id
Definition: qgsfeature.h:64
virtual double sample(const QgsPointXY &point, int band, bool *ok=nullptr, const QgsRectangle &boundingBox=QgsRectangle(), int width=0, int height=0, int dpi=96)
Samples a raster value from the specified band found at the point position.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
Wrapper for iterator of features from vector data provider or vector layer.
Circle geometry type.
Definition: qgscircle.h:43
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
Third quartile (numeric fields only)
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function&#39;s...
int precision
Single variable definition for use within a QgsExpressionContextScope.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn&#39;t create new instance)
Definition: qgsstyle.cpp:362
Base class for all map layer types.
Definition: qgsmaplayer.h:79
Inter quartile range (IQR) (numeric fields only)
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
QgsGeometry combine(const QgsGeometry &geometry) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
QVariantMap config() const
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource...
Median of values (numeric fields only)
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
double sum
The sum of all cells in the band. NO_DATA values are excluded.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
QgsMapLayerType type() const
Returns the type of the layer.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
bool isValid() const
Returns true if the interval is valid.
Definition: qgsinterval.h:164
Represents a color stop within a QgsGradientColorRamp color ramp.
Definition: qgscolorramp.h:101
int bandCount() const
Returns the number of bands in this layer.
int params() const
The number of parameters this function takes.
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
double months() const
Returns the interval duration in months (based on a 30 day month).
Definition: qgsinterval.h:86
virtual QgsDataProvider * dataProvider()
Returns the layer&#39;s data provider, it may be nullptr.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Multi point geometry collection.
Definition: qgsmultipoint.h:29
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string...
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
void setPrepareFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *)> &prepareFunc)
Set a function that will be called in the prepare step to determine if the function is static or not...
QVector< QgsRingSequence > QgsCoordinateSequence
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:331
First quartile (numeric fields only)
QStringList rights() const
Returns a list of attribution or copyright strings associated with the resource.
Number of missing (null) values.
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
QString abstract() const
Returns the abstract of the layer used by QGIS Server in GetCapabilities request. ...
Definition: qgsmaplayer.h:295
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
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
Abstract base class for color ramps.
Definition: qgscolorramp.h:31
QgsVectorLayer referencingLayer
Definition: qgsrelation.h:46
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
QVariant evaluate()
Evaluate the feature and return the result.
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
QString toProj4() const
Returns a Proj4 string representation of this CRS.
double maximumValue
The maximum cell value in the raster band.
double seconds() const
Returns the interval duration in seconds.
Definition: qgsinterval.h:151
Multi line string geometry collection.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
Curve polygon geometry type.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
Handles the with_variable(name, value, node) expression function.
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
double convertLengthMeasurement(double length, QgsUnitTypes::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
Handles the array_foreach(array, expression) expression function.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:438
QgsGeometry centroid() const
Returns the center of mass of a geometry.
QgsLayerMetadata metadata
Definition: qgsmaplayer.h:85
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition: qgsgeometry.h:80
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
Quadrilateral geometry type.
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
double convertAreaMeasurement(double area, QgsUnitTypes::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Triangle geometry type.
Definition: qgstriangle.h:33
QgsFields fields
Definition: qgsfeature.h:66
QgsStaticExpressionFunction(const QString &fnname, int params, FcnEval fcn, const QString &group, const QString &helpText=QString(), bool usesGeometry=false, const QSet< QString > &referencedColumns=QSet< QString >(), bool lazyEval=false, const QStringList &aliases=QStringList(), bool handlesNull=false)
Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter val...
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function&#39;s...
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest point on this geometry to another geometry.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
Format
Available formats for displaying coordinates.
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
QString name() const
The name of the column.
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Definition: qgscolorramp.h:127
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:154
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:74
virtual QgsRectangle extent() const
Returns the extent of the layer.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
QString what() const
Definition: qgsexception.h:48
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
static QString encodeColor(const QColor &color)
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QString errorMessage() const
Returns the most recent error message encountered by the database.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QVariant(* FcnEval)(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Function definition for evaluation against an expression context, using a list of values as parameter...
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
#define ENSURE_NO_EVAL_ERROR
QgsUnitTypes::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g., "$area".
double stdDev
The standard deviation of the cell values.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets feature ID that should be fetched.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
CORE_EXPORT QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
The RasterBandStats struct is a container for statistics about a single raster band.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
double mean
The mean cell value for the band. NO_DATA values are excluded.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
Utility class for identifying a unique vertex within a geometry.
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
Geometry collection.
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
static Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
Minimum length of string (string fields only)
virtual QgsPolygon * toPolygon(unsigned int segments=36) const
Returns a segmented polygon.
Definition: qgsellipse.cpp:224
const QgsExpressionFunction::ParameterList & parameters() const
Returns the list of named parameters for the function, if set.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry. ...
static QString geometryDisplayString(GeometryType type)
Returns a display string for a geometry type.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QString keywordList() const
Returns the keyword list of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:311
Buffer to left of line.
Definition: qgsgeometry.h:1094
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership...
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction *)
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Regular Polygon geometry type.
The simplification gives each point in a line an importance weighting, so that least important points...
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
QgsGeometry offsetCurve(double distance, int segments, JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
QString description() const
Returns the descriptive name of the CRS, e.g., "WGS 84" or "GDA 94 / Vicgrid94".
An expression node which takes it value from a feature&#39;s field.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode)
Construct a QgsQuadrilateral as a Rectangle from 3 points.
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example...
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle...
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
QgsVectorLayer referencedLayer
Definition: qgsrelation.h:47
Abstract base class for all nodes that can appear in an expression.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
Single scope for storing variables and functions for use within a QgsExpressionContext.
Abstract base class for all geometries.
An expression node for expression functions.
double minimumScale() const
Returns the minimum map scale (i.e.
Contains information about the context in which a coordinate transform is executed.
QgsRelationManager relationManager
Definition: qgsproject.h:100
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
double maximumScale() const
Returns the maximum map scale (i.e.
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
static const QList< QgsExpressionFunction * > & Functions()
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which correspond to this point projected by a specified distance with specified a...
Definition: qgspoint.cpp:675
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
Majority of values (numeric fields only)
bool lazyEval() const
true if this function should use lazy evaluation.
QString group() const
Returns the first group which the function belongs to.
QString abstract() const
Returns a free-form description of the resource.
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
double length() const
Returns the planar, 2-dimensional length of geometry.
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double x
Definition: qgspointxy.h:47
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:279
A field formatter helps to handle and display values for a field.
double weeks() const
Returns the interval duration in weeks.
Definition: qgsinterval.h:99
int numGeometries() const
Returns the number of geometries within the collection.
QgsExpressionFunction(const QString &fnname, int params, const QString &group, const QString &helpText=QString(), bool lazyEval=false, bool handlesNull=false, bool isContextual=false)
Constructor for function which uses unnamed parameters.
Use rounded joins.
Definition: qgsgeometry.h:1111
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition: qgscurve.cpp:40
uint qHash(const QVariant &variant)
Hash for QVariant.
Definition: qgis.cpp:252
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsUnitTypes::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e.g., "$length" and "$perimeter".
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QString asWkt(int precision=17) const
Exports the geometry to WKT.
QString name() const
The name of the function.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
A representation of the interval between two datetime values.
Definition: qgsinterval.h:39
Handles the array_filter(array, expression) expression function.
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
QVector< QgsPoint > QgsPointSequence
double days() const
Returns the interval duration in days.
Definition: qgsinterval.h:112
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:148
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations...
int partCount() const override
Returns count of parts contained in the geometry.
QVector< QgsPointSequence > QgsRingSequence
A abstract base class for defining QgsExpression functions.
A list of expression nodes.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects. ...
Maximum length of string (string fields only)
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
static QgsGeometry geometryFromGML(const QString &xmlString)
Static method that creates geometry from GML.
static bool allParamsStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context)
This will return true if all the params for the provided function node are static within the constrai...
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
bool isValid
Definition: qgsrelation.h:49
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
double years() const
Returns the interval duration in years (based on an average year length)
Definition: qgsinterval.h:73
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
An expression node for literal values.
Pad minute and second values with leading zeros, eg &#39;05&#39; instead of &#39;5&#39;.
Mean of values (numeric fields only)
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
const QString helpText() const
The help text for the function.
c++ helper class for defining QgsExpression functions.
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places...
Definition: qgis.h:328
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
This class allows including a set of layers in a database-side transaction, provided the layer data p...
Holder for the widget type and its configuration for a field.
void setIsStaticFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *) > &isStatic)
Set a function that will be called in the prepare step to determine if the function is static or not...
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
double minutes() const
Returns the interval duration in minutes.
Definition: qgsinterval.h:138
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
#define ENSURE_GEOM_TYPE(f, g, geomtype)
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:442
Create a multipart geometry from aggregated geometries.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
This class represents a coordinate reference system (CRS).
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function&#39;s...
double hours() const
Returns the interval duration in hours.
Definition: qgsinterval.h:125
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
double range
The range is the distance between min & max.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
Class for doing transforms between two map coordinate systems.
bool operator==(const QgsExpressionFunction &other) const
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
QgsGeometry singleSidedBuffer(double distance, int segments, BufferSide side, JoinStyle joinStyle=JoinStyleRound, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
Include a direction suffix (eg &#39;N&#39;, &#39;E&#39;, &#39;S&#39; or &#39;W&#39;), otherwise a "-" prefix is used for west and sou...
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
double minimumValue
The minimum cell value in the raster band.
static QString helpText(QString name)
Returns the help text for a specified function.
OperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
double z
Definition: qgspoint.h:43
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QString name
Definition: qgsmaplayer.h:83
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
Use beveled joins.
Definition: qgsgeometry.h:1113
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
Represents a single parameter passed to a function.
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:126
QgsGeometry geometry
Definition: qgsfeature.h:67
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
QgsPolygon * toPolygon() const
Returns as a polygon.
double area() const
Returns the planar, 2-dimensional area of the geometry.
int count() const
Returns the number of nodes in the list.
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
Ellipse geometry type.
Definition: qgsellipse.h:39
The geometries can be simplified using the current map2pixel context state.
bool nextFeature(QgsFeature &f)
QStringList aliases() const override
Returns a list of possible aliases for the function.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required...
Minority of values (numeric fields only)
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
JoinStyle
Join styles for buffers.
Definition: qgsgeometry.h:1109
Geometry is not required. It may still be returned if e.g. required for a filter condition.
const QgsCurve * exteriorRing() const
Returns the curve polygon&#39;s exterior ring.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Degrees and decimal minutes, eg 30degrees 45.55&#39;.
QgsGeometry symDifference(const QgsGeometry &geometry) const
Returns a geometry representing the points making up this geometry that do not make up other...
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
CORE_EXPORT QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string...
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:139
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
Range of values (max - min) (numeric and datetime fields only)
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
bool isEmpty() const override
Returns true if the geometry is empty.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
virtual bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
This will be called during the prepare step() of an expression if it is not static.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
QString authid() const
Returns the authority identifier for the CRS.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:86
Sample standard deviation of values (numeric fields only)
Aggregate
Available aggregates to calculate.
QString attribution() const
Returns the attribution of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:369
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
static QColor decodeColor(const QString &str)
Represents a list of OrderByClauses, with the most important first and the least important last...
virtual int partCount() const =0
Returns count of parts contained in the geometry.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
double m
Definition: qgspoint.h:44
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
#define FEAT_FROM_CONTEXT(c, f)
A bundle of parameters controlling aggregate calculation.
Degrees, minutes and seconds, eg 30 degrees 45&#39;30".
QVariant aggregate(QgsAggregateCalculator::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr) const
Calculates an aggregated value from the layer&#39;s features.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QgsGeometry difference(const QgsGeometry &geometry) const
Returns a geometry representing the points making up this geometry that do not make up other...