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