QGIS API Documentation  3.9.0-Master (04f0879ed5)
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" ) )</