QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 "qgscoordinateutils.h"
21 #include "qgsexpressionfunction.h"
22 #include "qgsexpressionutils.h"
23 #include "qgsexpressionnodeimpl.h"
24 #include "qgsexiftools.h"
25 #include "qgsfeaturerequest.h"
26 #include "qgsstringutils.h"
27 #include "qgsmultipoint.h"
28 #include "qgsgeometryutils.h"
29 #include "qgshstoreutils.h"
30 #include "qgsmultilinestring.h"
31 #include "qgslinestring.h"
32 #include "qgscurvepolygon.h"
34 #include "qgspolygon.h"
35 #include "qgstriangle.h"
36 #include "qgscurve.h"
37 #include "qgsregularpolygon.h"
38 #include "qgsquadrilateral.h"
39 #include "qgsmultipolygon.h"
40 #include "qgsogcutils.h"
41 #include "qgsdistancearea.h"
42 #include "qgsgeometryengine.h"
43 #include "qgsexpressionsorter.h"
44 #include "qgssymbollayerutils.h"
45 #include "qgsstyle.h"
46 #include "qgsexception.h"
47 #include "qgsmessagelog.h"
48 #include "qgsrasterlayer.h"
49 #include "qgsvectorlayer.h"
50 #include "qgsrasterbandstats.h"
51 #include "qgscolorramp.h"
53 #include "qgsfieldformatter.h"
55 #include "qgsproviderregistry.h"
56 #include "sqlite3.h"
57 #include "qgstransaction.h"
58 #include "qgsthreadingutils.h"
59 #include "qgsapplication.h"
60 #include "qgis.h"
62 #include "qgsunittypes.h"
63 #include "qgsspatialindex.h"
64 
65 #include <QMimeDatabase>
66 #include <QProcessEnvironment>
67 #include <QCryptographicHash>
68 #include <QRegularExpression>
69 #include <QUuid>
70 
71 typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
72 
73 Q_GLOBAL_STATIC( ExpressionFunctionList, sOwnedFunctions )
74 Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
76 
79 Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
80 
81 const QString QgsExpressionFunction::helpText() const
82 {
83  return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
84 }
85 
87 {
88  Q_UNUSED( node )
89  // evaluate arguments
90  QVariantList argValues;
91  if ( args )
92  {
93  int arg = 0;
94  const QList< QgsExpressionNode * > argList = args->list();
95  for ( QgsExpressionNode *n : argList )
96  {
97  QVariant v;
98  if ( lazyEval() )
99  {
100  // Pass in the node for the function to eval as it needs.
101  v = QVariant::fromValue( n );
102  }
103  else
104  {
105  v = n->eval( parent, context );
107  bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
108  if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
109  return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
110  }
111  argValues.append( v );
112  arg++;
113  }
114  }
115 
116  return func( argValues, context, parent, node );
117 }
118 
120 {
121  Q_UNUSED( node )
122  return true;
123 }
124 
126 {
127  return QStringList();
128 }
129 
131 {
132  Q_UNUSED( parent )
133  Q_UNUSED( context )
134  Q_UNUSED( node )
135  return false;
136 }
137 
139 {
140  Q_UNUSED( parent )
141  Q_UNUSED( context )
142  Q_UNUSED( node )
143  return true;
144 }
145 
147 {
148  Q_UNUSED( node )
149  return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
150 }
151 
153 {
154  return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
155 }
156 
158 {
159  return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
160 }
161 
163 {
164  return mHandlesNull;
165 }
166 
167 // doxygen doesn't like this constructor for some reason (maybe the function arguments?)
170  FcnEval fcn,
171  const QString &group,
172  const QString &helpText,
173  const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
174  const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
175  bool lazyEval,
176  const QStringList &aliases,
177  bool handlesNull )
178  : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
179  , mFnc( fcn )
180  , mAliases( aliases )
181  , mUsesGeometry( false )
182  , mUsesGeometryFunc( usesGeometry )
183  , mReferencedColumnsFunc( referencedColumns )
184 {
185 }
187 
189 {
190  return mAliases;
191 }
192 
194 {
195  if ( mUsesGeometryFunc )
196  return mUsesGeometryFunc( node );
197  else
198  return mUsesGeometry;
199 }
200 
202 {
203  if ( mReferencedColumnsFunc )
204  return mReferencedColumnsFunc( node );
205  else
206  return mReferencedColumns;
207 }
208 
210 {
211  if ( mIsStaticFunc )
212  return mIsStaticFunc( node, parent, context );
213  else
214  return mIsStatic;
215 }
216 
218 {
219  if ( mPrepareFunc )
220  return mPrepareFunc( node, parent, context );
221 
222  return true;
223 }
224 
226 {
227  mIsStaticFunc = isStatic;
228 }
229 
231 {
232  mIsStaticFunc = nullptr;
233  mIsStatic = isStatic;
234 }
235 
236 void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
237 {
238  mPrepareFunc = prepareFunc;
239 }
240 
242 {
243  if ( node && node->args() )
244  {
245  const QList< QgsExpressionNode * > argList = node->args()->list();
246  for ( QgsExpressionNode *argNode : argList )
247  {
248  if ( !argNode->isStatic( parent, context ) )
249  return false;
250  }
251  }
252 
253  return true;
254 }
255 
256 static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
257 {
258  double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
259  double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
260  double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
261 
262  if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
263  return QVariant();
264 
265  QVariantList array;
266  int length = 1;
267 
268  array << start;
269  double current = start + step;
270  while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
271  {
272  array << current;
273  current += step;
274  length++;
275  }
276 
277  return array;
278 }
279 
280 static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
281 {
282  if ( !context )
283  return QVariant();
284 
285  QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
286  return context->variable( name );
287 }
288 
289 static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
290 {
291  QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
292  return QgsExpression::replaceExpressionText( templateString, context );
293 }
294 
295 static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
296 {
297  if ( !context )
298  return QVariant();
299 
300  QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
301  QgsExpression expression( expString );
302  return expression.evaluate( context );
303 }
304 
305 static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
306 {
307  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
308  return QVariant( std::sqrt( x ) );
309 }
310 
311 static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
312 {
313  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
314  return QVariant( std::fabs( val ) );
315 }
316 
317 static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
318 {
319  double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
320  return ( deg * M_PI ) / 180;
321 }
322 static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
323 {
324  double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
325  return ( 180 * rad ) / M_PI;
326 }
327 static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
328 {
329  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
330  return QVariant( std::sin( x ) );
331 }
332 static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
333 {
334  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
335  return QVariant( std::cos( x ) );
336 }
337 static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
338 {
339  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
340  return QVariant( std::tan( x ) );
341 }
342 static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
343 {
344  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
345  return QVariant( std::asin( x ) );
346 }
347 static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
348 {
349  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
350  return QVariant( std::acos( x ) );
351 }
352 static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
353 {
354  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
355  return QVariant( std::atan( x ) );
356 }
357 static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
358 {
359  double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
360  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
361  return QVariant( std::atan2( y, x ) );
362 }
363 static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
364 {
365  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
366  return QVariant( std::exp( x ) );
367 }
368 static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
369 {
370  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
371  if ( x <= 0 )
372  return QVariant();
373  return QVariant( std::log( x ) );
374 }
375 static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
376 {
377  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
378  if ( x <= 0 )
379  return QVariant();
380  return QVariant( log10( x ) );
381 }
382 static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
383 {
384  double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
385  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
386  if ( x <= 0 || b <= 0 )
387  return QVariant();
388  return QVariant( std::log( x ) / std::log( b ) );
389 }
390 static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
391 {
392  double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
393  double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
394  if ( max < min )
395  return QVariant();
396 
397  std::random_device rd;
398  std::mt19937_64 generator( rd() );
399 
400  if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
401  {
402  quint32 seed;
403  if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
404  {
405  // if seed can be converted to int, we use as is
406  seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
407  }
408  else
409  {
410  // if not, we hash string representation to int
411  QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
412  std::hash<std::string> hasher;
413  seed = hasher( seedStr.toStdString() );
414  }
415  generator.seed( seed );
416  }
417 
418  // Return a random double in the range [min, max] (inclusive)
419  double f = static_cast< double >( generator() ) / static_cast< double >( generator.max() );
420  return QVariant( min + f * ( max - min ) );
421 }
422 static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
423 {
424  qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
425  qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
426  if ( max < min )
427  return QVariant();
428 
429  std::random_device rd;
430  std::mt19937_64 generator( rd() );
431 
432  if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
433  {
434  quint32 seed;
435  if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
436  {
437  // if seed can be converted to int, we use as is
438  seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
439  }
440  else
441  {
442  // if not, we hash string representation to int
443  QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
444  std::hash<std::string> hasher;
445  seed = hasher( seedStr.toStdString() );
446  }
447  generator.seed( seed );
448  }
449 
450  qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
451  if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
452  return QVariant( randomInteger );
453 
454  // Prevent wrong conversion of QVariant. See #36412
455  return QVariant( int( randomInteger ) );
456 }
457 
458 static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
459 {
460  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
461  double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
462  double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
463  double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
464  double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
465 
466  if ( domainMin >= domainMax )
467  {
468  parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
469  return QVariant();
470  }
471 
472  // outside of domain?
473  if ( val >= domainMax )
474  {
475  return rangeMax;
476  }
477  else if ( val <= domainMin )
478  {
479  return rangeMin;
480  }
481 
482  // calculate linear scale
483  double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
484  double c = rangeMin - ( domainMin * m );
485 
486  // Return linearly scaled value
487  return QVariant( m * val + c );
488 }
489 
490 static QVariant fcnExpScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
491 {
492  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
493  double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
494  double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
495  double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
496  double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
497  double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
498 
499  if ( domainMin >= domainMax )
500  {
501  parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
502  return QVariant();
503  }
504  if ( exponent <= 0 )
505  {
506  parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
507  return QVariant();
508  }
509 
510  // outside of domain?
511  if ( val >= domainMax )
512  {
513  return rangeMax;
514  }
515  else if ( val <= domainMin )
516  {
517  return rangeMin;
518  }
519 
520  // Return exponentially scaled value
521  return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
522 }
523 
524 static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
525 {
526  QVariant result( QVariant::Double );
527  double maxVal = 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( maxVal ) )
532  {
533  maxVal = testVal;
534  }
535  else if ( !std::isnan( testVal ) )
536  {
537  maxVal = std::max( maxVal, testVal );
538  }
539  }
540 
541  if ( !std::isnan( maxVal ) )
542  {
543  result = QVariant( maxVal );
544  }
545  return result;
546 }
547 
548 static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
549 {
550  QVariant result( QVariant::Double );
551  double minVal = std::numeric_limits<double>::quiet_NaN();
552  for ( const QVariant &val : values )
553  {
554  double testVal = val.isNull() ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
555  if ( std::isnan( minVal ) )
556  {
557  minVal = testVal;
558  }
559  else if ( !std::isnan( testVal ) )
560  {
561  minVal = std::min( minVal, testVal );
562  }
563  }
564 
565  if ( !std::isnan( minVal ) )
566  {
567  result = QVariant( minVal );
568  }
569  return result;
570 }
571 
572 static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
573 {
574  //lazy eval, so we need to evaluate nodes now
575 
576  //first node is layer id or name
577  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
579  QVariant value = node->eval( parent, context );
581  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, parent );
582  if ( !vl )
583  {
584  parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
585  return QVariant();
586  }
587 
588  // second node is aggregate type
589  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
591  value = node->eval( parent, context );
593  bool ok = false;
594  QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
595  if ( !ok )
596  {
597  parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
598  return QVariant();
599  }
600 
601  // third node is subexpression (or field name)
602  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
604  QString subExpression = node->dump();
605 
607  //optional forth node is filter
608  if ( values.count() > 3 )
609  {
610  node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
612  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
613  if ( !nl || nl->value().isValid() )
614  parameters.filter = node->dump();
615  }
616 
617  //optional fifth node is concatenator
618  if ( values.count() > 4 )
619  {
620  node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
622  value = node->eval( parent, context );
624  parameters.delimiter = value.toString();
625  }
626 
627  //optional sixth node is order by
628  QString orderBy;
629  if ( values.count() > 5 )
630  {
631  node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
633  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
634  if ( !nl || nl->value().isValid() )
635  {
636  orderBy = node->dump();
637  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
638  }
639  }
640 
641  QString aggregateError;
642  QVariant result;
643  if ( context )
644  {
645  QString cacheKey;
646  QgsExpression subExp( subExpression );
647  QgsExpression filterExp( parameters.filter );
648 
649  bool isStatic = true;
650  if ( filterExp.referencedVariables().contains( QStringLiteral( "parent" ) )
651  || filterExp.referencedVariables().contains( QString() )
652  || subExp.referencedVariables().contains( QStringLiteral( "parent" ) )
653  || subExp.referencedVariables().contains( QString() ) )
654  {
655  isStatic = false;
656  }
657  else
658  {
659  const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
660  for ( const QString &varName : refVars )
661  {
662  const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
663  if ( scope && !scope->isStatic( varName ) )
664  {
665  isStatic = false;
666  break;
667  }
668  }
669  }
670 
671  if ( !isStatic )
672  {
673  cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter,
674  QString::number( context->feature().id() ), QString::number( qHash( context->feature() ) ), orderBy );
675  }
676  else
677  {
678  cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter, orderBy );
679  }
680 
681  if ( context->hasCachedValue( cacheKey ) )
682  {
683  return context->cachedValue( cacheKey );
684  }
685 
686  QgsExpressionContext subContext( *context );
688  subScope->setVariable( QStringLiteral( "parent" ), context->feature() );
689  subContext.appendScope( subScope );
690  result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
691 
692  if ( ok )
693  {
694  // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
695  // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
696  // associated with it's calculation!
697  context->setCachedValue( cacheKey, result );
698  }
699  }
700  else
701  {
702  result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
703  }
704  if ( !ok )
705  {
706  if ( !aggregateError.isEmpty() )
707  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
708  else
709  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
710  return QVariant();
711  }
712 
713  return result;
714 }
715 
716 static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
717 {
718  if ( !context )
719  {
720  parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
721  return QVariant();
722  }
723 
724  // first step - find current layer
725  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
726  if ( !vl )
727  {
728  parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
729  return QVariant();
730  }
731 
732  //lazy eval, so we need to evaluate nodes now
733 
734  //first node is relation name
735  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
737  QVariant value = node->eval( parent, context );
739  QString relationId = value.toString();
740  // check relation exists
741  QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId );
742  if ( !relation.isValid() || relation.referencedLayer() != vl )
743  {
744  // check for relations by name
745  QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId );
746  if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
747  {
748  parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
749  return QVariant();
750  }
751  else
752  {
753  relation = relations.at( 0 );
754  }
755  }
756 
757  QgsVectorLayer *childLayer = relation.referencingLayer();
758 
759  // second node is aggregate type
760  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
762  value = node->eval( parent, context );
764  bool ok = false;
765  QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
766  if ( !ok )
767  {
768  parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
769  return QVariant();
770  }
771 
772  //third node is subexpression (or field name)
773  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
775  QString subExpression = node->dump();
776 
777  //optional fourth node is concatenator
779  if ( values.count() > 3 )
780  {
781  node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
783  value = node->eval( parent, context );
785  parameters.delimiter = value.toString();
786  }
787 
788  //optional fifth node is order by
789  QString orderBy;
790  if ( values.count() > 4 )
791  {
792  node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
794  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
795  if ( !nl || nl->value().isValid() )
796  {
797  orderBy = node->dump();
798  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
799  }
800  }
801 
802  if ( !context->hasFeature() )
803  return QVariant();
804  QgsFeature f = context->feature();
805 
806  parameters.filter = relation.getRelatedFeaturesFilter( f );
807 
808  QString cacheKey = QStringLiteral( "relagg:%1:%2:%3:%4:%5" ).arg( vl->id(),
809  QString::number( static_cast< int >( aggregate ) ),
810  subExpression,
811  parameters.filter,
812  orderBy );
813  if ( context->hasCachedValue( cacheKey ) )
814  return context->cachedValue( cacheKey );
815 
816  QVariant result;
817  ok = false;
818 
819 
820  QgsExpressionContext subContext( *context );
821  QString error;
822  result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
823 
824  if ( !ok )
825  {
826  if ( !error.isEmpty() )
827  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
828  else
829  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
830  return QVariant();
831  }
832 
833  // cache value
834  context->setCachedValue( cacheKey, result );
835  return result;
836 }
837 
838 
839 static QVariant fcnAggregateGeneric( QgsAggregateCalculator::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
840 {
841  if ( !context )
842  {
843  parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
844  return QVariant();
845  }
846 
847  // first step - find current layer
848  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
849  if ( !vl )
850  {
851  parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
852  return QVariant();
853  }
854 
855  //lazy eval, so we need to evaluate nodes now
856 
857  //first node is subexpression (or field name)
858  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
860  QString subExpression = node->dump();
861 
862  //optional second node is group by
863  QString groupBy;
864  if ( values.count() > 1 )
865  {
866  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
868  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
869  if ( !nl || nl->value().isValid() )
870  groupBy = node->dump();
871  }
872 
873  //optional third node is filter
874  if ( values.count() > 2 )
875  {
876  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
878  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
879  if ( !nl || nl->value().isValid() )
880  parameters.filter = node->dump();
881  }
882 
883  //optional order by node, if supported
884  QString orderBy;
885  if ( orderByPos >= 0 && values.count() > orderByPos )
886  {
887  node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
889  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
890  if ( !nl || nl->value().isValid() )
891  {
892  orderBy = node->dump();
893  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
894  }
895  }
896 
897  // build up filter with group by
898 
899  // find current group by value
900  if ( !groupBy.isEmpty() )
901  {
902  QgsExpression groupByExp( groupBy );
903  QVariant groupByValue = groupByExp.evaluate( context );
904  QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
905  groupByValue.isNull() ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
906  QgsExpression::quotedValue( groupByValue ) );
907  if ( !parameters.filter.isEmpty() )
908  parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
909  else
910  parameters.filter = groupByClause;
911  }
912 
913  QgsExpression subExp( subExpression );
914  QgsExpression filterExp( parameters.filter );
915 
916  bool isStatic = true;
917  const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
918  for ( const QString &varName : refVars )
919  {
920  const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
921  if ( scope && !scope->isStatic( varName ) )
922  {
923  isStatic = false;
924  break;
925  }
926  }
927 
928  QString cacheKey;
929  if ( !isStatic )
930  {
931  cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter,
932  QString::number( context->feature().id() ), QString::number( qHash( context->feature() ) ), orderBy );
933  }
934  else
935  {
936  cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter, orderBy );
937  }
938 
939  if ( context->hasCachedValue( cacheKey ) )
940  return context->cachedValue( cacheKey );
941 
942  QVariant result;
943  bool ok = false;
944 
945  QgsExpressionContext subContext( *context );
947  subScope->setVariable( QStringLiteral( "parent" ), context->feature() );
948  subContext.appendScope( subScope );
949  QString error;
950  result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
951 
952  if ( !ok )
953  {
954  if ( !error.isEmpty() )
955  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
956  else
957  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
958  return QVariant();
959  }
960 
961  // cache value
962  context->setCachedValue( cacheKey, result );
963  return result;
964 }
965 
966 
967 static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
968 {
969  return fcnAggregateGeneric( QgsAggregateCalculator::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
970 }
971 
972 static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
973 {
974  return fcnAggregateGeneric( QgsAggregateCalculator::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
975 }
976 
977 static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
978 {
979  return fcnAggregateGeneric( QgsAggregateCalculator::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
980 }
981 
982 static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
983 {
984  return fcnAggregateGeneric( QgsAggregateCalculator::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
985 }
986 
987 static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
988 {
989  return fcnAggregateGeneric( QgsAggregateCalculator::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
990 }
991 
992 static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
993 {
994  return fcnAggregateGeneric( QgsAggregateCalculator::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
995 }
996 
997 static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
998 {
999  return fcnAggregateGeneric( QgsAggregateCalculator::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1000 }
1001 
1002 static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1003 {
1004  return fcnAggregateGeneric( QgsAggregateCalculator::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1005 }
1006 
1007 static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1008 {
1009  return fcnAggregateGeneric( QgsAggregateCalculator::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1010 }
1011 
1012 static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1013 {
1014  return fcnAggregateGeneric( QgsAggregateCalculator::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1015 }
1016 
1017 static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1018 {
1019  return fcnAggregateGeneric( QgsAggregateCalculator::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1020 }
1021 
1022 static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1023 {
1024  return fcnAggregateGeneric( QgsAggregateCalculator::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1025 }
1026 
1027 static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1028 {
1029  return fcnAggregateGeneric( QgsAggregateCalculator::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1030 }
1031 
1032 static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1033 {
1034  return fcnAggregateGeneric( QgsAggregateCalculator::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1035 }
1036 
1037 static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1038 {
1039  return fcnAggregateGeneric( QgsAggregateCalculator::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1040 }
1041 
1042 static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1043 {
1044  return fcnAggregateGeneric( QgsAggregateCalculator::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1045 }
1046 
1047 static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1048 {
1049  return fcnAggregateGeneric( QgsAggregateCalculator::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1050 }
1051 
1052 static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1053 {
1054  return fcnAggregateGeneric( QgsAggregateCalculator::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1055 }
1056 
1057 static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1058 {
1060 
1061  //fourth node is concatenator
1062  if ( values.count() > 3 )
1063  {
1064  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1066  QVariant value = node->eval( parent, context );
1068  parameters.delimiter = value.toString();
1069  }
1070 
1071  return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenate, values, parameters, context, parent, 4 );
1072 }
1073 
1074 static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1075 {
1077 
1078  //fourth node is concatenator
1079  if ( values.count() > 3 )
1080  {
1081  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1083  QVariant value = node->eval( parent, context );
1085  parameters.delimiter = value.toString();
1086  }
1087 
1088  return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenateUnique, values, parameters, context, parent, 4 );
1089 }
1090 
1091 static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1092 {
1093  return fcnAggregateGeneric( QgsAggregateCalculator::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1094 }
1095 
1096 static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1097 {
1098  if ( !context )
1099  return QVariant();
1100 
1101  QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1102  bool ok = false;
1103  if ( !scale.isValid() || scale.isNull() )
1104  return QVariant();
1105 
1106  const double v = scale.toDouble( &ok );
1107  if ( ok )
1108  return v;
1109  return QVariant();
1110 }
1111 
1112 static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1113 {
1114  double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1115  double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1116  double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1117 
1118  // force testValue to sit inside the range specified by the min and max value
1119  if ( testValue <= minValue )
1120  {
1121  return QVariant( minValue );
1122  }
1123  else if ( testValue >= maxValue )
1124  {
1125  return QVariant( maxValue );
1126  }
1127  else
1128  {
1129  return QVariant( testValue );
1130  }
1131 }
1132 
1133 static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1134 {
1135  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1136  return QVariant( std::floor( x ) );
1137 }
1138 
1139 static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1140 {
1141  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1142  return QVariant( std::ceil( x ) );
1143 }
1144 
1145 static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1146 {
1147  return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1148 }
1149 static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1150 {
1151  return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1152 }
1153 static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1154 {
1155  return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1156 }
1157 
1158 static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1159 {
1160  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1161  QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1162  if ( format.isEmpty() && !language.isEmpty() )
1163  {
1164  parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1165  return QVariant( QDateTime() );
1166  }
1167 
1168  if ( format.isEmpty() && language.isEmpty() )
1169  return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1170 
1171  QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1172  QLocale locale = QLocale();
1173  if ( !language.isEmpty() )
1174  {
1175  locale = QLocale( language );
1176  }
1177 
1178  QDateTime datetime = locale.toDateTime( datetimestring, format );
1179  if ( !datetime.isValid() )
1180  {
1181  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1182  datetime = QDateTime();
1183  }
1184  return QVariant( datetime );
1185 }
1186 
1187 static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1188 {
1189  const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1190  const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1191  const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1192 
1193  const QDate date( year, month, day );
1194  if ( !date.isValid() )
1195  {
1196  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1197  return QVariant();
1198  }
1199  return QVariant( date );
1200 }
1201 
1202 static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1203 {
1204  const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1205  const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1206  const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1207 
1208  const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1209  if ( !time.isValid() )
1210  {
1211  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1212  return QVariant();
1213  }
1214  return QVariant( time );
1215 }
1216 
1217 static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1218 {
1219  const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1220  const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1221  const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1222  const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1223  const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1224  const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1225 
1226  const QDate date( year, month, day );
1227  if ( !date.isValid() )
1228  {
1229  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1230  return QVariant();
1231  }
1232  const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1233  if ( !time.isValid() )
1234  {
1235  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1236  return QVariant();
1237  }
1238  return QVariant( QDateTime( date, time ) );
1239 }
1240 
1241 static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1242 {
1243  const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1244  const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1245  const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1246  const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1247  const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1248  const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1249  const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1250 
1251  return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1252 }
1253 
1254 static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1255 {
1256  for ( const QVariant &value : values )
1257  {
1258  if ( value.isNull() )
1259  continue;
1260  return value;
1261  }
1262  return QVariant();
1263 }
1264 
1265 static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1266 {
1267  const QVariant val1 = values.at( 0 );
1268  const QVariant val2 = values.at( 1 );
1269 
1270  if ( val1 == val2 )
1271  return QVariant();
1272  else
1273  return val1;
1274 }
1275 
1276 static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1277 {
1278  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1279  return QVariant( str.toLower() );
1280 }
1281 static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1282 {
1283  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1284  return QVariant( str.toUpper() );
1285 }
1286 static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1287 {
1288  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1289  QStringList elems = str.split( ' ' );
1290  for ( int i = 0; i < elems.size(); i++ )
1291  {
1292  if ( elems[i].size() > 1 )
1293  elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1294  }
1295  return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1296 }
1297 
1298 static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1299 {
1300  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1301  return QVariant( str.trimmed() );
1302 }
1303 
1304 static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1305 {
1306  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1307  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1308  return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1309 }
1310 
1311 static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1312 {
1313  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1314  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1315  return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1316 }
1317 
1318 static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1319 {
1320  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1321  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1322  int dist = QgsStringUtils::hammingDistance( string1, string2 );
1323  return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1324 }
1325 
1326 static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1327 {
1328  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1329  return QVariant( QgsStringUtils::soundex( string ) );
1330 }
1331 
1332 static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1333 {
1334  QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1335  return QVariant( QString( character ) );
1336 }
1337 
1338 static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1339 {
1340  QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1341 
1342  if ( value.isEmpty() )
1343  {
1344  return QVariant();
1345  }
1346 
1347  int res = value.at( 0 ).unicode();
1348  return QVariant( res );
1349 }
1350 
1351 static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1352 {
1353  if ( values.length() == 2 || values.length() == 3 )
1354  {
1355  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1356  qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1357 
1358  QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1359 
1360  return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1361  }
1362 
1363  return QVariant();
1364 }
1365 
1366 static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1367 {
1368  // two variants, one for geometry, one for string
1369  if ( values.at( 0 ).canConvert<QgsGeometry>() )
1370  {
1371  //geometry variant
1372  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1373  if ( geom.type() != QgsWkbTypes::LineGeometry )
1374  return QVariant();
1375 
1376  return QVariant( geom.length() );
1377  }
1378 
1379  //otherwise fall back to string variant
1380  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1381  return QVariant( str.length() );
1382 }
1383 
1384 static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1385 {
1386  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1387 
1388  if ( geom.type() != QgsWkbTypes::LineGeometry )
1389  return QVariant();
1390 
1391  double totalLength = 0;
1392  for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1393  {
1394  if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *it ) )
1395  {
1396  totalLength += line->length3D();
1397  }
1398  else
1399  {
1400  std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1401  totalLength += segmentized->length3D();
1402  }
1403  }
1404 
1405  return totalLength;
1406 }
1407 
1408 static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1409 {
1410  if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
1411  {
1412  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1413  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1414  QVector< QPair< QString, QString > > mapItems;
1415 
1416  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1417  {
1418  mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1419  }
1420 
1421  // larger keys should be replaced first since they may contain whole smaller keys
1422  std::sort( mapItems.begin(),
1423  mapItems.end(),
1424  []( const QPair< QString, QString > &pair1,
1425  const QPair< QString, QString > &pair2 )
1426  {
1427  return ( pair1.first.length() > pair2.first.length() );
1428  } );
1429 
1430  for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1431  {
1432  str = str.replace( it->first, it->second );
1433  }
1434 
1435  return QVariant( str );
1436  }
1437  else if ( values.count() == 3 )
1438  {
1439  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1440  QVariantList before;
1441  QVariantList after;
1442  bool isSingleReplacement = false;
1443 
1444  if ( values.at( 1 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
1445  {
1446  before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1447  }
1448  else
1449  {
1450  before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1451  }
1452 
1453  if ( values.at( 2 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
1454  {
1455  after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1456  isSingleReplacement = true;
1457  }
1458  else
1459  {
1460  after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1461  }
1462 
1463  if ( !isSingleReplacement && before.length() != after.length() )
1464  {
1465  parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1466  return QVariant();
1467  }
1468 
1469  for ( int i = 0; i < before.length(); i++ )
1470  {
1471  str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1472  }
1473 
1474  return QVariant( str );
1475  }
1476  else
1477  {
1478  parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1479  return QVariant();
1480  }
1481 }
1482 
1483 static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1484 {
1485  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1486  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1487  QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1488 
1489  QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1490  if ( !re.isValid() )
1491  {
1492  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1493  return QVariant();
1494  }
1495  return QVariant( str.replace( re, after ) );
1496 }
1497 
1498 static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1499 {
1500  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1501  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1502 
1503  QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1504  if ( !re.isValid() )
1505  {
1506  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1507  return QVariant();
1508  }
1509  return QVariant( ( str.indexOf( re ) + 1 ) );
1510 }
1511 
1512 static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1513 {
1514  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1515  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1516  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1517 
1518  QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1519  if ( !re.isValid() )
1520  {
1521  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1522  return QVariant();
1523  }
1524 
1525  QRegularExpressionMatch matches = re.match( str );
1526  if ( matches.hasMatch() )
1527  {
1528  QVariantList array;
1529  QStringList list = matches.capturedTexts();
1530 
1531  // Skip the first string to only return captured groups
1532  for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1533  {
1534  array += ( !( *it ).isEmpty() ) ? *it : empty;
1535  }
1536 
1537  return QVariant( array );
1538  }
1539  else
1540  {
1541  return QVariant();
1542  }
1543 }
1544 
1545 static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1546 {
1547  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1548  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1549 
1550  QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1551  if ( !re.isValid() )
1552  {
1553  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1554  return QVariant();
1555  }
1556 
1557  // extract substring
1558  QRegularExpressionMatch match = re.match( str );
1559  if ( match.hasMatch() )
1560  {
1561  // return first capture
1562  if ( match.lastCapturedIndex() > 0 )
1563  {
1564  // a capture group was present, so use that
1565  return QVariant( match.captured( 1 ) );
1566  }
1567  else
1568  {
1569  // no capture group, so using all match
1570  return QVariant( match.captured( 0 ) );
1571  }
1572  }
1573  else
1574  {
1575  return QVariant( "" );
1576  }
1577 }
1578 
1579 static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1580 {
1581  QString uuid = QUuid::createUuid().toString();
1582  if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1583  uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1584  else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1585  uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1586  return uuid;
1587 }
1588 
1589 static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1590 {
1591  if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1592  return QVariant();
1593 
1594  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1595  int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1596 
1597  int len = 0;
1598  if ( values.at( 2 ).isValid() )
1599  len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1600  else
1601  len = str.size();
1602 
1603  if ( from < 0 )
1604  {
1605  from = str.size() + from;
1606  if ( from < 0 )
1607  {
1608  from = 0;
1609  }
1610  }
1611  else if ( from > 0 )
1612  {
1613  //account for the fact that substr() starts at 1
1614  from -= 1;
1615  }
1616 
1617  if ( len < 0 )
1618  {
1619  len = str.size() + len - from;
1620  if ( len < 0 )
1621  {
1622  len = 0;
1623  }
1624  }
1625 
1626  return QVariant( str.mid( from, len ) );
1627 }
1628 static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1629 {
1630  FEAT_FROM_CONTEXT( context, f )
1631  // TODO: handling of 64-bit feature ids?
1632  return QVariant( static_cast< int >( f.id() ) );
1633 }
1634 
1635 static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1636 {
1637  QgsRasterLayer *layer = QgsExpressionUtils::getRasterLayer( values.at( 0 ), parent );
1638  if ( !layer || !layer->dataProvider() )
1639  {
1640  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1641  return QVariant();
1642  }
1643 
1644  int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1645  if ( bandNb < 1 || bandNb > layer->bandCount() )
1646  {
1647  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1648  return QVariant();
1649  }
1650 
1651  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1652  if ( geom.isNull() || geom.type() != QgsWkbTypes::PointGeometry )
1653  {
1654  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1655  return QVariant();
1656  }
1657 
1658  QgsPointXY point = geom.asPoint();
1659  if ( geom.isMultipart() )
1660  {
1661  QgsMultiPointXY multiPoint = geom.asMultiPoint();
1662  if ( multiPoint.count() == 1 )
1663  {
1664  point = multiPoint[0];
1665  }
1666  else
1667  {
1668  // if the geometry contains more than one part, return an undefined value
1669  return QVariant();
1670  }
1671  }
1672 
1673  double value = layer->dataProvider()->sample( point, bandNb );
1674  return std::isnan( value ) ? QVariant() : value;
1675 }
1676 
1677 static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1678 {
1679  if ( !context )
1680  return QVariant();
1681 
1682  return context->feature();
1683 }
1684 
1685 static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1686 {
1687  QgsFeature feature;
1688  QString attr;
1689  if ( values.size() == 1 )
1690  {
1691  attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1692  feature = context->feature();
1693  }
1694  else if ( values.size() == 2 )
1695  {
1696  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1697  attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1698  }
1699  else
1700  {
1701  parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %1 given." ).arg( values.length() ) );
1702  return QVariant();
1703  }
1704 
1705  return feature.attribute( attr );
1706 }
1707 
1708 static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1709 {
1710  QgsFeature feature;
1711  if ( values.size() == 0 || values.at( 0 ).isNull() )
1712  {
1713  feature = context->feature();
1714  }
1715  else
1716  {
1717  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1718  }
1719 
1720  const QgsFields fields = feature.fields();
1721  QVariantMap result;
1722  for ( int i = 0; i < fields.count(); ++i )
1723  {
1724  result.insert( fields.at( i ).name(), feature.attribute( i ) );
1725  }
1726  return result;
1727 }
1728 
1729 static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
1730 {
1731  QgsVectorLayer *layer = nullptr;
1732  QgsFeature feature;
1733  bool evaluate = true;
1734 
1735  if ( values.isEmpty() )
1736  {
1737  feature = context->feature();
1738  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1739  }
1740  else if ( values.size() == 1 )
1741  {
1742  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1743  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1744  }
1745  else if ( values.size() == 2 )
1746  {
1747  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1748  feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1749  }
1750  else if ( values.size() == 3 )
1751  {
1752  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1753  feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1754  evaluate = values.value( 2 ).toBool();
1755  }
1756  else
1757  {
1758  if ( isMaptip )
1759  {
1760  parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %1 given." ).arg( values.length() ) );
1761  }
1762  else
1763  {
1764  parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %1 given." ).arg( values.length() ) );
1765  }
1766  return QVariant();
1767  }
1768 
1769  if ( !layer )
1770  {
1771  parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
1772  return QVariant( );
1773  }
1774 
1775  if ( !feature.isValid() )
1776  {
1777  parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
1778  return QVariant( );
1779  }
1780 
1781  if ( ! evaluate )
1782  {
1783  if ( isMaptip )
1784  {
1785  return layer->mapTipTemplate();
1786  }
1787  else
1788  {
1789  return layer->displayExpression();
1790  }
1791  }
1792 
1793  QgsExpressionContext subContext( *context );
1794  subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
1795  subContext.setFeature( feature );
1796 
1797  if ( isMaptip )
1798  {
1799  return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
1800  }
1801  else
1802  {
1803  QgsExpression exp( layer->displayExpression() );
1804  exp.prepare( &subContext );
1805  return exp.evaluate( &subContext ).toString();
1806  }
1807 }
1808 
1809 static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1810 {
1811  return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
1812 }
1813 
1814 static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1815 {
1816  return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
1817 }
1818 
1819 static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1820 {
1821  QgsVectorLayer *layer = nullptr;
1822  QgsFeature feature;
1823 
1824  if ( values.isEmpty() )
1825  {
1826  feature = context->feature();
1827  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1828  }
1829  else if ( values.size() == 1 )
1830  {
1831  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1832  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1833  }
1834  else if ( values.size() == 2 )
1835  {
1836  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1837  feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1838  }
1839  else
1840  {
1841  parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %1 given." ).arg( values.length() ) );
1842  return QVariant();
1843  }
1844 
1845  if ( !layer || !feature.isValid() )
1846  {
1847  return QVariant( QVariant::Bool );
1848  }
1849 
1850  return layer->selectedFeatureIds().contains( feature.id() );
1851 }
1852 
1853 static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1854 {
1855  QgsVectorLayer *layer = nullptr;
1856 
1857  if ( values.isEmpty() )
1858  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1859  else if ( values.count() == 1 )
1860  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1861  else
1862  {
1863  parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %1 given." ).arg( values.length() ) );
1864  return QVariant();
1865  }
1866 
1867  if ( !layer )
1868  {
1869  return QVariant( QVariant::LongLong );
1870  }
1871 
1872  return layer->selectedFeatureCount();
1873 }
1874 
1875 static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1876 {
1877  static QMap<QString, qlonglong> counterCache;
1878  QVariant functionResult;
1879 
1880  std::function<void()> fetchAndIncrementFunc = [ =, &functionResult ]()
1881  {
1882  QString database;
1883  const QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1884 
1885  if ( layer )
1886  {
1887  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
1888  database = decodedUri.value( QStringLiteral( "path" ) ).toString();
1889  if ( database.isEmpty() )
1890  {
1891  parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
1892  }
1893  }
1894  else
1895  {
1896  database = values.at( 0 ).toString();
1897  }
1898 
1899  const QString table = values.at( 1 ).toString();
1900  const QString idColumn = values.at( 2 ).toString();
1901  const QString filterAttribute = values.at( 3 ).toString();
1902  const QVariant filterValue = values.at( 4 ).toString();
1903  const QVariantMap defaultValues = values.at( 5 ).toMap();
1904 
1905  // read from database
1906  sqlite3_database_unique_ptr sqliteDb;
1907  sqlite3_statement_unique_ptr sqliteStatement;
1908 
1909  if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
1910  {
1911  parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
1912  functionResult = QVariant();
1913  return;
1914  }
1915 
1916  QString errorMessage;
1917  QString currentValSql;
1918 
1919  qlonglong nextId = 0;
1920  bool cachedMode = false;
1921  bool valueRetrieved = false;
1922 
1923  QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
1924 
1925  // Running in transaction mode, check for cached value first
1926  if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
1927  {
1928  cachedMode = true;
1929 
1930  auto cachedCounter = counterCache.find( cacheString );
1931 
1932  if ( cachedCounter != counterCache.end() )
1933  {
1934  qlonglong &cachedValue = cachedCounter.value();
1935  nextId = cachedValue;
1936  nextId += 1;
1937  cachedValue = nextId;
1938  valueRetrieved = true;
1939  }
1940  }
1941 
1942  // Either not in cached mode or no cached value found, obtain from DB
1943  if ( !cachedMode || !valueRetrieved )
1944  {
1945  int result = SQLITE_ERROR;
1946 
1947  currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
1948  if ( !filterAttribute.isNull() )
1949  {
1950  currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
1951  }
1952 
1953  sqliteStatement = sqliteDb.prepare( currentValSql, result );
1954 
1955  if ( result == SQLITE_OK )
1956  {
1957  nextId = 0;
1958  if ( sqliteStatement.step() == SQLITE_ROW )
1959  {
1960  nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
1961  }
1962 
1963  // If in cached mode: add value to cache and connect to transaction
1964  if ( cachedMode && result == SQLITE_OK )
1965  {
1966  counterCache.insert( cacheString, nextId );
1967 
1968  QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
1969  {
1970  counterCache.remove( cacheString );
1971  } );
1972  }
1973  valueRetrieved = true;
1974  }
1975  }
1976 
1977  if ( valueRetrieved )
1978  {
1979  QString upsertSql;
1980  upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
1981  QStringList cols;
1982  QStringList vals;
1983  cols << QgsSqliteUtils::quotedIdentifier( idColumn );
1984  vals << QgsSqliteUtils::quotedValue( nextId );
1985 
1986  if ( !filterAttribute.isNull() )
1987  {
1988  cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
1989  vals << QgsSqliteUtils::quotedValue( filterValue );
1990  }
1991 
1992  for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
1993  {
1994  cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
1995  vals << iter.value().toString();
1996  }
1997 
1998  upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
1999  upsertSql += QLatin1String( " VALUES " );
2000  upsertSql += '(' + vals.join( ',' ) + ')';
2001 
2002  int result = SQLITE_ERROR;
2003  if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2004  {
2005  QgsTransaction *transaction = layer->dataProvider()->transaction();
2006  if ( transaction->executeSql( upsertSql, errorMessage ) )
2007  {
2008  result = SQLITE_OK;
2009  }
2010  }
2011  else
2012  {
2013  result = sqliteDb.exec( upsertSql, errorMessage );
2014  }
2015  if ( result == SQLITE_OK )
2016  {
2017  functionResult = QVariant( nextId );
2018  return;
2019  }
2020  else
2021  {
2022  parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2023  functionResult = QVariant();
2024  return;
2025  }
2026  }
2027 
2028  functionResult = QVariant();
2029  };
2030 
2031  QgsThreadingUtils::runOnMainThread( fetchAndIncrementFunc );
2032 
2033  return functionResult;
2034 }
2035 
2036 static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2037 {
2038  QString concat;
2039  for ( const QVariant &value : values )
2040  {
2041  if ( !value.isNull() )
2042  concat += QgsExpressionUtils::getStringValue( value, parent );
2043  }
2044  return concat;
2045 }
2046 
2047 static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2048 {
2049  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2050  return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2051 }
2052 
2053 static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2054 {
2055  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2056  int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2057  return string.right( pos );
2058 }
2059 
2060 static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2061 {
2062  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2063  int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2064  return string.left( pos );
2065 }
2066 
2067 static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2068 {
2069  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2070  int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2071  QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2072  return string.leftJustified( length, fill.at( 0 ), true );
2073 }
2074 
2075 static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2076 {
2077  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2078  int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2079  QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2080  return string.rightJustified( length, fill.at( 0 ), true );
2081 }
2082 
2083 static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2084 {
2085  if ( values.size() < 1 )
2086  {
2087  parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2088  return QVariant();
2089  }
2090 
2091  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2092  for ( int n = 1; n < values.length(); n++ )
2093  {
2094  string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2095  }
2096  return string;
2097 }
2098 
2099 
2100 static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2101 {
2102  return QVariant( QDateTime::currentDateTime() );
2103 }
2104 
2105 static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2106 {
2107  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2108  QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2109  if ( format.isEmpty() && !language.isEmpty() )
2110  {
2111  parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2112  return QVariant( QDate() );
2113  }
2114 
2115  if ( format.isEmpty() && language.isEmpty() )
2116  return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2117 
2118  QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2119  QLocale locale = QLocale();
2120  if ( !language.isEmpty() )
2121  {
2122  locale = QLocale( language );
2123  }
2124 
2125  QDate date = locale.toDate( datestring, format );
2126  if ( !date.isValid() )
2127  {
2128  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2129  date = QDate();
2130  }
2131  return QVariant( date );
2132 }
2133 
2134 static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2135 {
2136  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2137  QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2138  if ( format.isEmpty() && !language.isEmpty() )
2139  {
2140  parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2141  return QVariant( QTime() );
2142  }
2143 
2144  if ( format.isEmpty() && language.isEmpty() )
2145  return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2146 
2147  QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2148  QLocale locale = QLocale();
2149  if ( !language.isEmpty() )
2150  {
2151  locale = QLocale( language );
2152  }
2153 
2154  QTime time = locale.toTime( timestring, format );
2155  if ( !time.isValid() )
2156  {
2157  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2158  time = QTime();
2159  }
2160  return QVariant( time );
2161 }
2162 
2163 static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2164 {
2165  return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2166 }
2167 
2168 /*
2169  * DMS functions
2170  */
2171 
2172 static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2173 {
2174  double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2175  QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2176  int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2177 
2178  QString formatString;
2179  if ( values.count() > 3 )
2180  formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2181 
2182  QgsCoordinateFormatter::FormatFlags flags = QgsCoordinateFormatter::FormatFlags();
2183  if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2184  {
2186  }
2187  else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2188  {
2190  }
2191  else if ( ! formatString.isEmpty() )
2192  {
2193  parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2194  return QVariant();
2195  }
2196 
2197  if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2198  {
2199  return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2200  }
2201  else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2202  {
2203  return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2204  }
2205  else
2206  {
2207  parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2208  return QVariant();
2209  }
2210 }
2211 
2212 static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2213 {
2215  return floatToDegreeFormat( format, values, context, parent, node );
2216 }
2217 
2218 static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2219 {
2220  double value = 0.0;
2221  bool ok = false;
2222  value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2223 
2224  return ok ? QVariant( value ) : QVariant();
2225 }
2226 
2227 static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2228 {
2230  return floatToDegreeFormat( format, values, context, parent, node );
2231 }
2232 
2233 static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2234 {
2235  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2236  QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2237  qint64 seconds = d2.secsTo( d1 );
2238  return QVariant::fromValue( QgsInterval( seconds ) );
2239 }
2240 
2241 static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2242 {
2243  if ( !values.at( 0 ).canConvert<QDate>() )
2244  return QVariant();
2245 
2246  QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2247  if ( !date.isValid() )
2248  return QVariant();
2249 
2250  // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2251  // (to match PostgreSQL behavior)
2252  return date.dayOfWeek() % 7;
2253 }
2254 
2255 static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2256 {
2257  QVariant value = values.at( 0 );
2258  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2259  if ( inter.isValid() )
2260  {
2261  return QVariant( inter.days() );
2262  }
2263  else
2264  {
2265  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2266  return QVariant( d1.date().day() );
2267  }
2268 }
2269 
2270 static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2271 {
2272  QVariant value = values.at( 0 );
2273  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2274  if ( inter.isValid() )
2275  {
2276  return QVariant( inter.years() );
2277  }
2278  else
2279  {
2280  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2281  return QVariant( d1.date().year() );
2282  }
2283 }
2284 
2285 static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2286 {
2287  QVariant value = values.at( 0 );
2288  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2289  if ( inter.isValid() )
2290  {
2291  return QVariant( inter.months() );
2292  }
2293  else
2294  {
2295  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2296  return QVariant( d1.date().month() );
2297  }
2298 }
2299 
2300 static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2301 {
2302  QVariant value = values.at( 0 );
2303  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2304  if ( inter.isValid() )
2305  {
2306  return QVariant( inter.weeks() );
2307  }
2308  else
2309  {
2310  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2311  return QVariant( d1.date().weekNumber() );
2312  }
2313 }
2314 
2315 static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2316 {
2317  QVariant value = values.at( 0 );
2318  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2319  if ( inter.isValid() )
2320  {
2321  return QVariant( inter.hours() );
2322  }
2323  else
2324  {
2325  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2326  return QVariant( t1.hour() );
2327  }
2328 }
2329 
2330 static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2331 {
2332  QVariant value = values.at( 0 );
2333  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2334  if ( inter.isValid() )
2335  {
2336  return QVariant( inter.minutes() );
2337  }
2338  else
2339  {
2340  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2341  return QVariant( t1.minute() );
2342  }
2343 }
2344 
2345 static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2346 {
2347  QVariant value = values.at( 0 );
2348  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2349  if ( inter.isValid() )
2350  {
2351  return QVariant( inter.seconds() );
2352  }
2353  else
2354  {
2355  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2356  return QVariant( t1.second() );
2357  }
2358 }
2359 
2360 static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2361 {
2362  QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2363  if ( dt.isValid() )
2364  {
2365  return QVariant( dt.toMSecsSinceEpoch() );
2366  }
2367  else
2368  {
2369  return QVariant();
2370  }
2371 }
2372 
2373 static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2374 {
2375  long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2376  // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2377  return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2378 }
2379 
2380 static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2381 {
2382  QString filepath = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2383  QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2384  return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
2385 }
2386 
2387 static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2388 {
2389  QString filepath = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2390  bool ok;
2391  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
2392 }
2393 
2394 #define ENSURE_GEOM_TYPE(f, g, geomtype) \
2395  if ( !(f).hasGeometry() ) \
2396  return QVariant(); \
2397  QgsGeometry g = (f).geometry(); \
2398  if ( (g).type() != (geomtype) ) \
2399  return QVariant();
2400 
2401 static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2402 {
2403  FEAT_FROM_CONTEXT( context, f )
2405  if ( g.isMultipart() )
2406  {
2407  return g.asMultiPoint().at( 0 ).x();
2408  }
2409  else
2410  {
2411  return g.asPoint().x();
2412  }
2413 }
2414 
2415 static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2416 {
2417  FEAT_FROM_CONTEXT( context, f )
2419  if ( g.isMultipart() )
2420  {
2421  return g.asMultiPoint().at( 0 ).y();
2422  }
2423  else
2424  {
2425  return g.asPoint().y();
2426  }
2427 }
2428 
2429 static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2430 {
2431  FEAT_FROM_CONTEXT( context, f )
2433 
2434  if ( g.isEmpty() )
2435  return QVariant();
2436 
2437  const QgsAbstractGeometry *abGeom = g.constGet();
2438 
2439  if ( g.isEmpty() || !abGeom->is3D() )
2440  return QVariant();
2441 
2442  if ( g.type() == QgsWkbTypes::PointGeometry && !g.isMultipart() )
2443  {
2444  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
2445  if ( point )
2446  return point->z();
2447  }
2448  else if ( g.type() == QgsWkbTypes::PointGeometry && g.isMultipart() )
2449  {
2450  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
2451  {
2452  if ( collection->numGeometries() > 0 )
2453  {
2454  if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2455  return point->z();
2456  }
2457  }
2458  }
2459 
2460  return QVariant();
2461 }
2462 
2463 static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2464 {
2465  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2466  if ( geom.isNull() )
2467  return QVariant();
2468 
2469  bool isValid = geom.isGeosValid();
2470 
2471  return QVariant( isValid );
2472 }
2473 
2474 static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2475 {
2476  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2477  if ( geom.isNull() )
2478  return QVariant();
2479 
2480  //if single point, return the point's x coordinate
2481  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2482  {
2483  return geom.asPoint().x();
2484  }
2485 
2486  //otherwise return centroid x
2487  QgsGeometry centroid = geom.centroid();
2488  QVariant result( centroid.asPoint().x() );
2489  return result;
2490 }
2491 
2492 static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2493 {
2494  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2495  if ( geom.isNull() )
2496  return QVariant();
2497 
2498  //if single point, return the point's y coordinate
2499  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2500  {
2501  return geom.asPoint().y();
2502  }
2503 
2504  //otherwise return centroid y
2505  QgsGeometry centroid = geom.centroid();
2506  QVariant result( centroid.asPoint().y() );
2507  return result;
2508 }
2509 
2510 static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2511 {
2512  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2513  if ( geom.isNull() )
2514  return QVariant(); //or 0?
2515 
2516  if ( !geom.constGet()->is3D() )
2517  return QVariant();
2518 
2519  //if single point, return the point's z coordinate
2520  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2521  {
2522  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2523  if ( point )
2524  return point->z();
2525  }
2526  else if ( geom.type() == QgsWkbTypes::PointGeometry && geom.isMultipart() )
2527  {
2528  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2529  {
2530  if ( collection->numGeometries() == 1 )
2531  {
2532  if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2533  return point->z();
2534  }
2535  }
2536  }
2537 
2538  return QVariant();
2539 }
2540 
2541 static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2542 {
2543  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2544  if ( geom.isNull() )
2545  return QVariant(); //or 0?
2546 
2547  if ( !geom.constGet()->isMeasure() )
2548  return QVariant();
2549 
2550  //if single point, return the point's m value
2551  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2552  {
2553  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2554  if ( point )
2555  return point->m();
2556  }
2557  else if ( geom.type() == QgsWkbTypes::PointGeometry && geom.isMultipart() )
2558  {
2559  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2560  {
2561  if ( collection->numGeometries() == 1 )
2562  {
2563  if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2564  return point->m();
2565  }
2566  }
2567  }
2568 
2569  return QVariant();
2570 }
2571 
2572 static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2573 {
2574  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2575 
2576  if ( geom.isNull() )
2577  return QVariant();
2578 
2579  int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2580 
2581  if ( idx < 0 )
2582  {
2583  //negative idx
2584  int count = geom.constGet()->nCoordinates();
2585  idx = count + idx;
2586  }
2587  else
2588  {
2589  //positive idx is 1 based
2590  idx -= 1;
2591  }
2592 
2593  QgsVertexId vId;
2594  if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
2595  {
2596  parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
2597  return QVariant();
2598  }
2599 
2600  QgsPoint point = geom.constGet()->vertexAt( vId );
2601  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2602 }
2603 
2604 static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2605 {
2606  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2607 
2608  if ( geom.isNull() )
2609  return QVariant();
2610 
2611  QgsVertexId vId;
2612  if ( !geom.vertexIdFromVertexNr( 0, vId ) )
2613  {
2614  return QVariant();
2615  }
2616 
2617  QgsPoint point = geom.constGet()->vertexAt( vId );
2618  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2619 }
2620 
2621 static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2622 {
2623  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2624 
2625  if ( geom.isNull() )
2626  return QVariant();
2627 
2628  QgsVertexId vId;
2629  if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
2630  {
2631  return QVariant();
2632  }
2633 
2634  QgsPoint point = geom.constGet()->vertexAt( vId );
2635  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2636 }
2637 
2638 static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2639 {
2640  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2641 
2642  if ( geom.isNull() )
2643  return QVariant();
2644 
2645  bool ignoreClosing = false;
2646  if ( values.length() > 1 )
2647  {
2648  ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
2649  }
2650 
2651  QgsMultiPoint *mp = new QgsMultiPoint();
2652 
2653  const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
2654  for ( const QgsRingSequence &part : sequence )
2655  {
2656  for ( const QgsPointSequence &ring : part )
2657  {
2658  bool skipLast = false;
2659  if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
2660  {
2661  skipLast = true;
2662  }
2663 
2664  for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
2665  {
2666  mp->addGeometry( ring.at( i ).clone() );
2667  }
2668  }
2669  }
2670 
2671  return QVariant::fromValue( QgsGeometry( mp ) );
2672 }
2673 
2674 static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2675 {
2676  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2677 
2678  if ( geom.isNull() )
2679  return QVariant();
2680 
2681  const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
2682 
2683  //OK, now we have a complete list of segmentized lines from the geometry
2685  for ( QgsLineString *line : linesToProcess )
2686  {
2687  for ( int i = 0; i < line->numPoints() - 1; ++i )
2688  {
2690  segment->setPoints( QgsPointSequence()
2691  << line->pointN( i )
2692  << line->pointN( i + 1 ) );
2693  ml->addGeometry( segment );
2694  }
2695  delete line;
2696  }
2697 
2698  return QVariant::fromValue( QgsGeometry( ml ) );
2699 }
2700 
2701 static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2702 {
2703  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2704 
2705  if ( geom.isNull() )
2706  return QVariant();
2707 
2708  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
2709  if ( !curvePolygon && geom.isMultipart() )
2710  {
2711  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2712  {
2713  if ( collection->numGeometries() == 1 )
2714  {
2715  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
2716  }
2717  }
2718  }
2719 
2720  if ( !curvePolygon )
2721  return QVariant();
2722 
2723  //idx is 1 based
2724  qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
2725 
2726  if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
2727  return QVariant();
2728 
2729  QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
2730  QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
2731  return result;
2732 }
2733 
2734 static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2735 {
2736  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2737 
2738  if ( geom.isNull() )
2739  return QVariant();
2740 
2741  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
2742  if ( !collection )
2743  return QVariant();
2744 
2745  //idx is 1 based
2746  qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
2747 
2748  if ( idx < 0 || idx >= collection->numGeometries() )
2749  return QVariant();
2750 
2751  QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
2752  QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
2753  return result;
2754 }
2755 
2756 static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2757 {
2758  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2759 
2760  if ( geom.isNull() )
2761  return QVariant();
2762 
2763  QgsAbstractGeometry *boundary = geom.constGet()->boundary();
2764  if ( !boundary )
2765  return QVariant();
2766 
2767  return QVariant::fromValue( QgsGeometry( boundary ) );
2768 }
2769 
2770 static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2771 {
2772  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2773 
2774  if ( geom.isNull() )
2775  return QVariant();
2776 
2777  QgsGeometry merged = geom.mergeLines();
2778  if ( merged.isNull() )
2779  return QVariant();
2780 
2781  return QVariant::fromValue( merged );
2782 }
2783 
2784 static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2785 {
2786  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2787 
2788  if ( geom.isNull() )
2789  return QVariant();
2790 
2791  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2792 
2793  QgsGeometry simplified = geom.simplify( tolerance );
2794  if ( simplified.isNull() )
2795  return QVariant();
2796 
2797  return simplified;
2798 }
2799 
2800 static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2801 {
2802  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2803 
2804  if ( geom.isNull() )
2805  return QVariant();
2806 
2807  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2808 
2810 
2811  QgsGeometry simplified = simplifier.simplify( geom );
2812  if ( simplified.isNull() )
2813  return QVariant();
2814 
2815  return simplified;
2816 }
2817 
2818 static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2819 {
2820  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2821 
2822  if ( geom.isNull() )
2823  return QVariant();
2824 
2825  int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
2826  double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
2827  double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
2828  double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
2829 
2830  QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
2831  if ( smoothed.isNull() )
2832  return QVariant();
2833 
2834  return smoothed;
2835 }
2836 
2837 static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2838 {
2839  QVariantList list;
2840  if ( values.size() == 1 && ( values.at( 0 ).type() == QVariant::List || values.at( 0 ).type() == QVariant::StringList ) )
2841  {
2842  list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
2843  }
2844  else
2845  {
2846  list = values;
2847  }
2848 
2849  QVector< QgsGeometry > parts;
2850  parts.reserve( list.size() );
2851  for ( const QVariant &value : std::as_const( list ) )
2852  {
2853  if ( value.canConvert<QgsGeometry>() )
2854  {
2855  parts << value.value<QgsGeometry>();
2856  }
2857  else
2858  {
2859  parent->setEvalErrorString( QStringLiteral( "Cannot convert to geometry" ) );
2860  return QgsGeometry();
2861  }
2862  }
2863 
2864  return QgsGeometry::collectGeometry( parts );
2865 }
2866 
2867 static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2868 {
2869  if ( values.count() < 2 || values.count() > 4 )
2870  {
2871  parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
2872  return QVariant();
2873  }
2874 
2875  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2876  double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2877  double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
2878  double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
2879  switch ( values.count() )
2880  {
2881  case 2:
2882  return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
2883  case 3:
2884  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointZ, x, y, z ) ) );
2885  case 4:
2886  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointZM, x, y, z, m ) ) );
2887  }
2888  return QVariant(); //avoid warning
2889 }
2890 
2891 static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2892 {
2893  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2894  double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2895  double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2896  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointM, x, y, 0.0, m ) ) );
2897 }
2898 
2899 static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2900 {
2901  if ( values.empty() )
2902  {
2903  return QVariant();
2904  }
2905 
2906  QVector<QgsPoint> points;
2907  points.reserve( values.count() );
2908 
2909  auto addPoint = [&points]( const QgsGeometry & geom )
2910  {
2911  if ( geom.isNull() )
2912  return;
2913 
2914  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
2915  return;
2916 
2917  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2918  if ( !point )
2919  return;
2920 
2921  points << *point;
2922  };
2923 
2924  for ( const QVariant &value : values )
2925  {
2926  if ( value.type() == QVariant::List )
2927  {
2928  const QVariantList list = value.toList();
2929  for ( const QVariant &v : list )
2930  {
2931  addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
2932  }
2933  }
2934  else
2935  {
2936  addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
2937  }
2938  }
2939 
2940  if ( points.count() < 2 )
2941  return QVariant();
2942 
2943  return QgsGeometry( new QgsLineString( points ) );
2944 }
2945 
2946 static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2947 {
2948  if ( values.count() < 1 )
2949  {
2950  parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
2951  return QVariant();
2952  }
2953 
2954  QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2955 
2956  if ( outerRing.type() == QgsWkbTypes::PolygonGeometry )
2957  return outerRing; // if it's already a polygon we have nothing to do
2958 
2959  if ( outerRing.type() != QgsWkbTypes::LineGeometry || outerRing.isNull() )
2960  return QVariant();
2961 
2962  std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
2963 
2964  const QgsCurve *exteriorRing = qgsgeometry_cast< QgsCurve * >( outerRing.constGet() );
2965  if ( !exteriorRing && outerRing.isMultipart() )
2966  {
2967  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
2968  {
2969  if ( collection->numGeometries() == 1 )
2970  {
2971  exteriorRing = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
2972  }
2973  }
2974  }
2975 
2976  if ( !exteriorRing )
2977  return QVariant();
2978 
2979  polygon->setExteriorRing( exteriorRing->segmentize() );
2980 
2981 
2982  for ( int i = 1; i < values.count(); ++i )
2983  {
2984  QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
2985  if ( ringGeom.isNull() )
2986  continue;
2987 
2988  if ( ringGeom.type() != QgsWkbTypes::LineGeometry || ringGeom.isNull() )
2989  continue;
2990 
2991  const QgsCurve *ring = qgsgeometry_cast< QgsCurve * >( ringGeom.constGet() );
2992  if ( !ring && ringGeom.isMultipart() )
2993  {
2994  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
2995  {
2996  if ( collection->numGeometries() == 1 )
2997  {
2998  ring = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
2999  }
3000  }
3001  }
3002 
3003  if ( !ring )
3004  continue;
3005 
3006  polygon->addInteriorRing( ring->segmentize() );
3007  }
3008 
3009  return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
3010 }
3011 
3012 static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3013 {
3014  std::unique_ptr<QgsTriangle> tr( new QgsTriangle() );
3015  std::unique_ptr<QgsLineString> lineString( new QgsLineString() );
3016  lineString->clear();
3017 
3018  for ( const QVariant &value : values )
3019  {
3020  QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
3021  if ( geom.isNull() )
3022  return QVariant();
3023 
3024  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
3025  return QVariant();
3026 
3027  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3028  if ( !point && geom.isMultipart() )
3029  {
3030  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3031  {
3032  if ( collection->numGeometries() == 1 )
3033  {
3034  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3035  }
3036  }
3037  }
3038 
3039  if ( !point )
3040  return QVariant();
3041 
3042  lineString->addVertex( *point );
3043  }
3044 
3045  tr->setExteriorRing( lineString.release() );
3046 
3047  return QVariant::fromValue( QgsGeometry( tr.release() ) );
3048 }
3049 
3050 static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3051 {
3052  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3053  if ( geom.isNull() )
3054  return QVariant();
3055 
3056  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
3057  return QVariant();
3058 
3059  double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3060  int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3061 
3062  if ( segment < 3 )
3063  {
3064  parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3065  return QVariant();
3066  }
3067  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3068  if ( !point && geom.isMultipart() )
3069  {
3070  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3071  {
3072  if ( collection->numGeometries() == 1 )
3073  {
3074  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3075  }
3076  }
3077  }
3078  if ( !point )
3079  return QVariant();
3080 
3081  QgsCircle circ( *point, radius );
3082  return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3083 }
3084 
3085 static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3086 {
3087  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3088  if ( geom.isNull() )
3089  return QVariant();
3090 
3091  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
3092  return QVariant();
3093 
3094  double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3095  double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3096  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3097  int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
3098  if ( segment < 3 )
3099  {
3100  parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3101  return QVariant();
3102  }
3103  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3104  if ( !point && geom.isMultipart() )
3105  {
3106  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3107  {
3108  if ( collection->numGeometries() == 1 )
3109  {
3110  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3111  }
3112  }
3113  }
3114  if ( !point )
3115  return QVariant();
3116 
3117  QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
3118  return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3119 }
3120 
3121 static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3122 {
3123 
3124  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3125  if ( pt1.isNull() )
3126  return QVariant();
3127 
3128  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3129  return QVariant();
3130 
3131  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3132  if ( pt2.isNull() )
3133  return QVariant();
3134 
3135  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3136  return QVariant();
3137 
3138  unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
3139  if ( nbEdges < 3 )
3140  {
3141  parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
3142  return QVariant();
3143  }
3144 
3145  QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3147  {
3148  parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
3149  return QVariant();
3150  }
3151 
3152  const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
3153  if ( !center && pt1.isMultipart() )
3154  {
3155  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
3156  {
3157  if ( collection->numGeometries() == 1 )
3158  {
3159  center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3160  }
3161  }
3162  }
3163  if ( !center )
3164  return QVariant();
3165 
3166  const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
3167  if ( !corner && pt2.isMultipart() )
3168  {
3169  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
3170  {
3171  if ( collection->numGeometries() == 1 )
3172  {
3173  corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3174  }
3175  }
3176  }
3177  if ( !corner )
3178  return QVariant();
3179 
3180  QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
3181 
3182  return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
3183 
3184 }
3185 
3186 static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3187 {
3188  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3189  if ( pt1.isNull() )
3190  return QVariant();
3191  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3192  return QVariant();
3193 
3194  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3195  if ( pt2.isNull() )
3196  return QVariant();
3197  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3198  return QVariant();
3199 
3200  const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
3201  const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
3202  QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
3203 
3204  return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
3205 }
3206 
3207 static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3208 {
3209  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3210  if ( pt1.isNull() )
3211  return QVariant();
3212  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3213  return QVariant();
3214 
3215  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3216  if ( pt2.isNull() )
3217  return QVariant();
3218  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3219  return QVariant();
3220 
3221  QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
3222  if ( pt3.isNull() )
3223  return QVariant();
3224  if ( pt3.type() != QgsWkbTypes::PointGeometry || pt3.isMultipart() )
3225  return QVariant();
3226 
3227  QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3228  if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
3229  {
3230  parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
3231  return QVariant();
3232  }
3233  const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
3234  const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
3235  const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
3236  QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
3237  return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
3238 }
3239 
3240 static QVariant pointAt( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) // helper function
3241 {
3242  FEAT_FROM_CONTEXT( context, f )
3243  int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
3244  QgsGeometry g = f.geometry();
3245  if ( g.isNull() )
3246  return QVariant();
3247 
3248  if ( idx < 0 )
3249  {
3250  idx += g.constGet()->nCoordinates();
3251  }
3252  if ( idx < 0 || idx >= g.constGet()->nCoordinates() )
3253  {
3254  parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
3255  return QVariant();
3256  }
3257 
3258  QgsPointXY p = g.vertexAt( idx );
3259  return QVariant( QPointF( p.x(), p.y() ) );
3260 }
3261 
3262 static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )
3263 {
3264  QVariant v = pointAt( values, f, parent );
3265  if ( v.type() == QVariant::PointF )
3266  return QVariant( v.toPointF().x() );
3267  else
3268  return QVariant();
3269 }
3270 static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )
3271 {
3272  QVariant v = pointAt( values, f, parent );
3273  if ( v.type() == QVariant::PointF )
3274  return QVariant( v.toPointF().y() );
3275  else
3276  return QVariant();
3277 }
3278 static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3279 {
3280  FEAT_FROM_CONTEXT( context, f )
3281  QgsGeometry geom = f.geometry();
3282  if ( !geom.isNull() )
3283  return QVariant::fromValue( geom );
3284  else
3285  return QVariant();
3286 }
3287 
3288 static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3289 {
3290  QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3291  QgsGeometry geom = QgsGeometry::fromWkt( wkt );
3292  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3293  return result;
3294 }
3295 
3296 static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3297 {
3298  const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
3299  if ( wkb.isNull() )
3300  return QVariant();
3301 
3302  QgsGeometry geom;
3303  geom.fromWkb( wkb );
3304  return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3305 }
3306 
3307 static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3308 {
3309  QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3310  QgsOgcUtils::Context ogcContext;
3311  if ( context )
3312  {
3313  QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
3314  if ( mapLayerPtr )
3315  {
3316  ogcContext.layer = mapLayerPtr.data();
3317  ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
3318  }
3319  }
3320  QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
3321  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3322  return result;
3323 }
3324 
3325 static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3326 {
3327  FEAT_FROM_CONTEXT( context, f )
3329  QgsDistanceArea *calc = parent->geomCalculator();
3330  if ( calc )
3331  {
3332  double area = calc->measureArea( f.geometry() );
3333  area = calc->convertAreaMeasurement( area, parent->areaUnits() );
3334  return QVariant( area );
3335  }
3336  else
3337  {
3338  return QVariant( f.geometry().area() );
3339  }
3340 }
3341 
3342 static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3343 {
3344  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3345 
3346  if ( geom.type() != QgsWkbTypes::PolygonGeometry )
3347  return QVariant();
3348 
3349  return QVariant( geom.area() );
3350 }
3351 
3352 static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3353 {
3354  FEAT_FROM_CONTEXT( context, f )
3356  QgsDistanceArea *calc = parent->geomCalculator();
3357  if ( calc )
3358  {
3359  double len = calc->measureLength( f.geometry() );
3360  len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
3361  return QVariant( len );
3362  }
3363  else
3364  {
3365  return QVariant( f.geometry().length() );
3366  }
3367 }
3368 
3369 static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3370 {
3371  FEAT_FROM_CONTEXT( context, f )
3373  QgsDistanceArea *calc = parent->geomCalculator();
3374  if ( calc )
3375  {
3376  double len = calc->measurePerimeter( f.geometry() );
3377  len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
3378  return QVariant( len );
3379  }
3380  else
3381  {
3382  return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
3383  }
3384 }
3385 
3386 static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3387 {
3388  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3389 
3390  if ( geom.type() != QgsWkbTypes::PolygonGeometry )
3391  return QVariant();
3392 
3393  //length for polygons = perimeter
3394  return QVariant( geom.length() );
3395 }
3396 
3397 static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3398 {
3399  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3400  return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
3401 }
3402 
3403 static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3404 {
3405  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3406  if ( geom.isNull() )
3407  return QVariant();
3408 
3409  return QVariant( geom.constGet()->partCount() );
3410 }
3411 
3412 static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3413 {
3414  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3415  if ( geom.isNull() )
3416  return QVariant();
3417 
3418  return QVariant( geom.isMultipart() );
3419 }
3420 
3421 static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3422 {
3423  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3424 
3425  if ( geom.isNull() )
3426  return QVariant();
3427 
3428  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3429  if ( curvePolygon )
3430  return QVariant( curvePolygon->numInteriorRings() );
3431 
3432  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3433  if ( collection )
3434  {
3435  //find first CurvePolygon in collection
3436  for ( int i = 0; i < collection->numGeometries(); ++i )
3437  {
3438  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
3439  if ( !curvePolygon )
3440  continue;
3441 
3442  return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
3443  }
3444  }
3445 
3446  return QVariant();
3447 }
3448 
3449 static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3450 {
3451  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3452 
3453  if ( geom.isNull() )
3454  return QVariant();
3455 
3456  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3457  if ( curvePolygon )
3458  return QVariant( curvePolygon->ringCount() );
3459 
3460  bool foundPoly = false;
3461  int ringCount = 0;
3462  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3463  if ( collection )
3464  {
3465  //find CurvePolygons in collection
3466  for ( int i = 0; i < collection->numGeometries(); ++i )
3467  {
3468  curvePolygon = qgsgeometry_cast< QgsCurvePolygon *>( collection->geometryN( i ) );
3469  if ( !curvePolygon )
3470  continue;
3471 
3472  foundPoly = true;
3473  ringCount += curvePolygon->ringCount();
3474  }
3475  }
3476 
3477  if ( !foundPoly )
3478  return QVariant();
3479 
3480  return QVariant( ringCount );
3481 }
3482 
3483 static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3484 {
3485  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3486  QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
3487  QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
3488  return result;
3489 }
3490 
3491 static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3492 {
3493  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3494  return QVariant::fromValue( geom.boundingBox().width() );
3495 }
3496 
3497 static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3498 {
3499  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3500  return QVariant::fromValue( geom.boundingBox().height() );
3501 }
3502 
3503 static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3504 {
3505  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3506  if ( geom.isNull() )
3507  return QVariant();
3508 
3509  return QgsWkbTypes::geometryDisplayString( geom.type() );
3510 }
3511 
3512 static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3513 {
3514  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3515  return QVariant::fromValue( geom.boundingBox().xMinimum() );
3516 }
3517 
3518 static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3519 {
3520  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3521  return QVariant::fromValue( geom.boundingBox().xMaximum() );
3522 }
3523 
3524 static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3525 {
3526  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3527  return QVariant::fromValue( geom.boundingBox().yMinimum() );
3528 }
3529 
3530 static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3531 {
3532  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3533  return QVariant::fromValue( geom.boundingBox().yMaximum() );
3534 }
3535 
3536 static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3537 {
3538  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3539 
3540  if ( geom.isNull() || geom.isEmpty( ) )
3541  return QVariant();
3542 
3543  if ( !geom.constGet()->is3D() )
3544  return QVariant();
3545 
3546  double max = std::numeric_limits< double >::lowest();
3547 
3548  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3549  {
3550  double z = ( *it ).z();
3551 
3552  if ( max < z )
3553  max = z;
3554  }
3555 
3556  if ( max == std::numeric_limits< double >::lowest() )
3557  return QVariant( QVariant::Double );
3558 
3559  return QVariant( max );
3560 }
3561 
3562 static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3563 {
3564  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3565 
3566  if ( geom.isNull() || geom.isEmpty() )
3567  return QVariant();
3568 
3569  if ( !geom.constGet()->is3D() )
3570  return QVariant();
3571 
3572  double min = std::numeric_limits< double >::max();
3573 
3574  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3575  {
3576  double z = ( *it ).z();
3577 
3578  if ( z < min )
3579  min = z;
3580  }
3581 
3582  if ( min == std::numeric_limits< double >::max() )
3583  return QVariant( QVariant::Double );
3584 
3585  return QVariant( min );
3586 }
3587 
3588 static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3589 {
3590  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3591 
3592  if ( geom.isNull() || geom.isEmpty() )
3593  return QVariant();
3594 
3595  if ( !geom.constGet()->isMeasure() )
3596  return QVariant();
3597 
3598  double min = std::numeric_limits< double >::max();
3599 
3600  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3601  {
3602  double m = ( *it ).m();
3603 
3604  if ( m < min )
3605  min = m;
3606  }
3607 
3608  if ( min == std::numeric_limits< double >::max() )
3609  return QVariant( QVariant::Double );
3610 
3611  return QVariant( min );
3612 }
3613 
3614 static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3615 {
3616  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3617 
3618  if ( geom.isNull() || geom.isEmpty() )
3619  return QVariant();
3620 
3621  if ( !geom.constGet()->isMeasure() )
3622  return QVariant();
3623 
3624  double max = std::numeric_limits< double >::lowest();
3625 
3626  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3627  {
3628  double m = ( *it ).m();
3629 
3630  if ( max < m )
3631  max = m;
3632  }
3633 
3634  if ( max == std::numeric_limits< double >::lowest() )
3635  return QVariant( QVariant::Double );
3636 
3637  return QVariant( max );
3638 }
3639 
3640 static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3641 {
3642  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3643  const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom.constGet() );
3644  if ( !curve )
3645  {
3646  parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
3647  return QVariant();
3648  }
3649 
3650  return QVariant( curve->sinuosity() );
3651 }
3652 
3653 static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3654 {
3655  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3656  const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
3657  if ( !curve )
3658  {
3659  parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
3660  return QVariant();
3661  }
3662 
3663  return QVariant( curve->straightDistance2d() );
3664 }
3665 
3666 
3667 
3668 static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3669 {
3670  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3671  if ( geom.isNull() )
3672  return QVariant();
3673 
3674  std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
3675  flipped->swapXy();
3676  return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
3677 }
3678 
3679 static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3680 {
3681  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3682  if ( fGeom.isNull() )
3683  return QVariant();
3684 
3685  const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
3686  if ( !curve && fGeom.isMultipart() )
3687  {
3688  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
3689  {
3690  if ( collection->numGeometries() == 1 )
3691  {
3692  curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
3693  }
3694  }
3695  }
3696 
3697  if ( !curve )
3698  return QVariant();
3699 
3700  return QVariant::fromValue( curve->isClosed() );
3701 }
3702 
3703 static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3704 {
3705  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3706 
3707  if ( geom.isNull() )
3708  return QVariant();
3709 
3710  QVariant result;
3711  if ( !geom.isMultipart() )
3712  {
3713  const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
3714 
3715  if ( !line )
3716  return QVariant();
3717 
3718  std::unique_ptr< QgsLineString > closedLine( line->clone() );
3719  closedLine->close();
3720 
3721  result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
3722  }
3723  else
3724  {
3725  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
3726 
3727  std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
3728 
3729  for ( int i = 0; i < collection->numGeometries(); ++i )
3730  {
3731  if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
3732  {
3733  std::unique_ptr< QgsLineString > closedLine( line->clone() );
3734  closedLine->close();
3735 
3736  closed->addGeometry( closedLine.release() );
3737  }
3738  }
3739  result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
3740  }
3741 
3742  return result;
3743 }
3744 
3745 static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3746 {
3747  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3748  if ( fGeom.isNull() )
3749  return QVariant();
3750 
3751  return QVariant::fromValue( fGeom.isEmpty() );
3752 }
3753 
3754 static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3755 {
3756  if ( values.at( 0 ).isNull() )
3757  return QVariant::fromValue( true );
3758 
3759  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3760  return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
3761 }
3762 
3763 static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3764 {
3765  if ( values.length() < 2 || values.length() > 3 )
3766  return QVariant();
3767 
3768  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3769  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3770 
3771  if ( fGeom.isNull() || sGeom.isNull() )
3772  return QVariant();
3773 
3774  std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
3775 
3776  if ( values.length() == 2 )
3777  {
3778  //two geometry arguments, return relation
3779  QString result = engine->relate( sGeom.constGet() );
3780  return QVariant::fromValue( result );
3781  }
3782  else
3783  {
3784  //three arguments, test pattern
3785  QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3786  bool result = engine->relatePattern( sGeom.constGet(), pattern );
3787  return QVariant::fromValue( result );
3788  }
3789 }
3790 
3791 static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3792 {
3793  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3794  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3795  return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
3796 }
3797 static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3798 {
3799  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3800  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3801  return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
3802 }
3803 static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3804 {
3805  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3806  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3807  return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
3808 }
3809 static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3810 {
3811  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3812  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3813  return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
3814 }
3815 static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3816 {
3817  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3818  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3819  return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
3820 }
3821 static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3822 {
3823  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3824  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3825  return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
3826 }
3827 static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3828 {
3829  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3830  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3831  return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
3832 }
3833 static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3834 {
3835  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3836  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3837  return fGeom.within( sGeom ) ? TVL_True : TVL_False;
3838 }
3839 
3840 static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3841 {
3842  const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3843  const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3844  const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3845  const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3846  const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3847  const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3848 
3849  Qgis::EndCapStyle capStyle = Qgis::EndCapStyle::Round;
3850  if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
3851  capStyle = Qgis::EndCapStyle::Flat;
3852  else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
3853  capStyle = Qgis::EndCapStyle::Square;
3854 
3855  Qgis::JoinStyle joinStyle = Qgis::JoinStyle::Round;
3856  if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
3857  joinStyle = Qgis::JoinStyle::Miter;
3858  else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
3859  joinStyle = Qgis::JoinStyle::Bevel;
3860 
3861  QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
3862  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3863  return result;
3864 }
3865 
3866 static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3867 {
3868  const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3869  const QgsGeometry reoriented = fGeom.forceRHR();
3870  return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
3871 }
3872 
3873 static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3874 {
3875  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3876  const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
3877  if ( !pt && fGeom.isMultipart() )
3878  {
3879  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
3880  {
3881  if ( collection->numGeometries() == 1 )
3882  {
3883  pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3884  }
3885  }
3886  }
3887 
3888  if ( !pt )
3889  {
3890  parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
3891  return QVariant();
3892  }
3893 
3894  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3895  double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3896  double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3897  double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3898 
3899  QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
3900  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3901  return result;
3902 }
3903 
3904 static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3905 {
3906  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3907  if ( fGeom.type() != QgsWkbTypes::LineGeometry )
3908  {
3909  parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
3910  return QVariant();
3911  }
3912 
3913  double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3914  double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3915  int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3916 
3917  QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
3918  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3919  return result;
3920 }
3921 
3922 static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3923 {
3924  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3925  if ( fGeom.type() != QgsWkbTypes::LineGeometry )
3926  {
3927  parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
3928  return QVariant();
3929  }
3930 
3931  int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
3932 
3933  QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
3934  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3935  return result;
3936 }
3937 
3938 static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3939 {
3940  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3941  double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3942  int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3943  const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3944  if ( joinInt < 1 || joinInt > 3 )
3945  return QVariant();
3946  const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
3947 
3948  double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3949 
3950  QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
3951  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3952  return result;
3953 }
3954 
3955 static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3956 {
3957  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3958  double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3959  int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3960 
3961  const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3962  if ( joinInt < 1 || joinInt > 3 )
3963  return QVariant();
3964  const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
3965 
3966  double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3967 
3968  QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
3969  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3970  return result;
3971 }
3972 
3973 static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3974 {
3975  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3976  double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3977  double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3978 
3979  QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
3980  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3981  return result;
3982 }
3983 
3984 static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3985 {
3986  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3987  double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3988  double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3989  fGeom.translate( dx, dy );
3990  return QVariant::fromValue( fGeom );
3991 }
3992 
3993 static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3994 {
3995  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3996  const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3997  const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
3998  : QgsGeometry();
3999 
4000  QgsPointXY pt;
4001  if ( center.isNull() )
4002  {
4003  // if center wasn't specified, use bounding box centroid
4004  pt = fGeom.boundingBox().center();
4005  }
4006  else if ( center.type() != QgsWkbTypes::PointGeometry )
4007  {
4008  parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
4009  return QVariant();
4010  }
4011  else if ( center.isMultipart() )
4012  {
4013  QgsMultiPointXY multiPoint = center.asMultiPoint();
4014  if ( multiPoint.count() == 1 )
4015  {
4016  pt = multiPoint[0];
4017  }
4018  else
4019  {
4020  parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
4021  return QVariant();
4022  }
4023  }
4024  else
4025  {
4026  pt = center.asPoint();
4027  }
4028 
4029  fGeom.rotate( rotation, pt );
4030  return QVariant::fromValue( fGeom );
4031 }
4032 
4033 static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4034 {
4035  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4036  if ( fGeom.isNull() )
4037  {
4038  return QVariant();
4039  }
4040 
4041  const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4042  const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4043 
4044  const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4045 
4046  const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4047  const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4048 
4049  const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
4050  const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
4051  const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
4052  const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
4053 
4054  if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
4055  {
4056  fGeom.get()->addZValue( 0 );
4057  }
4058  if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
4059  {
4060  fGeom.get()->addMValue( 0 );
4061  }
4062 
4063  QTransform transform;
4064  transform.translate( deltaX, deltaY );
4065  transform.rotate( rotationZ );
4066  transform.scale( scaleX, scaleY );
4067  fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
4068 
4069  return QVariant::fromValue( fGeom );
4070 }
4071 
4072 
4073 static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4074 {
4075  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4076  QgsGeometry geom = fGeom.centroid();
4077  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4078  return result;
4079 }
4080 static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4081 {
4082  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4083  QgsGeometry geom = fGeom.pointOnSurface();
4084  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4085  return result;
4086 }
4087 
4088 static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4089 {
4090  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4091  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4092  QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
4093  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4094  return result;
4095 }
4096 
4097 static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4098 {
4099  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4100  QgsGeometry geom = fGeom.convexHull();
4101  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4102  return result;
4103 }
4104 
4105 
4106 static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4107 {
4108  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4109  int segments = 36;
4110  if ( values.length() == 2 )
4111  segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4112  if ( segments < 0 )
4113  {
4114  parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
4115  return QVariant();
4116  }
4117 
4118  QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
4119  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4120  return result;
4121 }
4122 
4123 static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4124 {
4125  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4126  QgsGeometry geom = fGeom.orientedMinimumBoundingBox( );
4127  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4128  return result;
4129 }
4130 
4131 static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4132 {
4133  const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4134 
4135  // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
4136  // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
4137  // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
4138 
4139  double area, angle, width, height;
4140  const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
4141 
4142  if ( geom.isNull() )
4143  {
4144  parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
4145  return QVariant();
4146  }
4147  return angle;
4148 }
4149 
4150 static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4151 {
4152  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4153  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4154  QgsGeometry geom = fGeom.difference( sGeom );
4155  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4156  return result;
4157 }
4158 
4159 static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4160 {
4161  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4162  if ( fGeom.isNull() )
4163  return QVariant();
4164 
4165  QVariant result;
4166  if ( !fGeom.isMultipart() )
4167  {
4168  const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
4169  if ( !curve )
4170  return QVariant();
4171 
4172  QgsCurve *reversed = curve->reversed();
4173  result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
4174  }
4175  else
4176  {
4177  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
4178  std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
4179  for ( int i = 0; i < collection->numGeometries(); ++i )
4180  {
4181  if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
4182  {
4183  reversed->addGeometry( curve->reversed() );
4184  }
4185  else
4186  {
4187  reversed->addGeometry( collection->geometryN( i )->clone() );
4188  }
4189  }
4190  result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
4191  }
4192  return result;
4193 }
4194 
4195 static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4196 {
4197  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4198  if ( fGeom.isNull() )
4199  return QVariant();
4200 
4201  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
4202  if ( !curvePolygon && fGeom.isMultipart() )
4203  {
4204  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4205  {
4206  if ( collection->numGeometries() == 1 )
4207  {
4208  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
4209  }
4210  }
4211  }
4212 
4213  if ( !curvePolygon || !curvePolygon->exteriorRing() )
4214  return QVariant();
4215 
4216  QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
4217  QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
4218  return result;
4219 }
4220 
4221 static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4222 {
4223  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4224  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4225  return QVariant( fGeom.distance( sGeom ) );
4226 }
4227 
4228 static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4229 {
4230  QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4231  QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4232 
4233  double res = -1;
4234  if ( values.length() == 3 && values.at( 2 ).isValid() )
4235  {
4236  double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4237  densify = std::clamp( densify, 0.0, 1.0 );
4238  res = g1.hausdorffDistanceDensify( g2, densify );
4239  }
4240  else
4241  {
4242  res = g1.hausdorffDistance( g2 );
4243  }
4244 
4245  return res > -1 ? QVariant( res ) : QVariant();
4246 }
4247 
4248 static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4249 {
4250  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4251  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4252  QgsGeometry geom = fGeom.intersection( sGeom );
4253  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4254  return result;
4255 }
4256 static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4257 {
4258  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4259  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4260  QgsGeometry geom = fGeom.symDifference( sGeom );
4261  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4262  return result;
4263 }
4264 static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4265 {
4266  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4267  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4268  QgsGeometry geom = fGeom.combine( sGeom );
4269  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4270  return result;
4271 }
4272 
4273 static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4274 {
4275  if ( values.length() < 1 || values.length() > 2 )
4276  return QVariant();
4277 
4278  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4279  int prec = 8;
4280  if ( values.length() == 2 )
4281  prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4282  QString wkt = fGeom.asWkt( prec );
4283  return QVariant( wkt );
4284 }
4285 
4286 static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4287 {
4288  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4289  return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
4290 }
4291 
4292 static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4293 {
4294  if ( values.length() != 2 )
4295  {
4296  parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %1 given." ).arg( values.length() ) );
4297  return QVariant();
4298  }
4299 
4300  QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4301  QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4302 
4303  const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
4304  if ( !pt1 && fGeom1.isMultipart() )
4305  {
4306  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
4307  {
4308  if ( collection->numGeometries() == 1 )
4309  {
4310  pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4311  }
4312  }
4313  }
4314 
4315  const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
4316  if ( !pt2 && fGeom2.isMultipart() )
4317  {
4318  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
4319  {
4320  if ( collection->numGeometries() == 1 )
4321  {
4322  pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4323  }
4324  }
4325  }
4326 
4327  if ( !pt1 || !pt2 )
4328  {
4329  parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
4330  return QVariant();
4331  }
4332 
4333  // Code from PostGIS
4334  if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
4335  {
4336  if ( pt1->y() < pt2->y() )
4337  return 0.0;
4338  else if ( pt1->y() > pt2->y() )
4339  return M_PI;
4340  else
4341  return 0;
4342  }
4343 
4344  if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
4345  {
4346  if ( pt1->x() < pt2->x() )
4347  return M_PI_2;
4348  else if ( pt1->x() > pt2->x() )
4349  return M_PI + ( M_PI_2 );
4350  else
4351  return 0;
4352  }
4353 
4354  if ( pt1->x() < pt2->x() )
4355  {
4356  if ( pt1->y() < pt2->y() )
4357  {
4358  return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
4359  }
4360  else /* ( pt1->y() > pt2->y() ) - equality case handled above */
4361  {
4362  return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
4363  + ( M_PI_2 );
4364  }
4365  }
4366 
4367  else /* ( pt1->x() > pt2->x() ) - equality case handled above */
4368  {
4369  if ( pt1->y() > pt2->y() )
4370  {
4371  return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
4372  + M_PI;
4373  }
4374  else /* ( pt1->y() < pt2->y() ) - equality case handled above */
4375  {
4376  return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
4377  + ( M_PI + ( M_PI_2 ) );
4378  }
4379  }
4380 }
4381 
4382 static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4383 {
4384  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4385 
4386  if ( ! geom.constGet() || QgsWkbTypes::flatType( geom.constGet()->simplifiedTypeRef( )->wkbType() ) != QgsWkbTypes::Type::Point )
4387  {
4388  parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
4389  return QVariant();
4390  }
4391 
4392  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4393  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4394  double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4395 
4396  const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
4397  QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
4398 
4399  return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
4400 }
4401 
4402 static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4403 {
4404  QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4405  QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4406 
4407  const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
4408  if ( !pt1 && fGeom1.isMultipart() )
4409  {
4410  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
4411  {
4412  if ( collection->numGeometries() == 1 )
4413  {
4414  pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4415  }
4416  }
4417  }
4418  const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
4419  if ( !pt2 && fGeom2.isMultipart() )
4420  {
4421  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
4422  {
4423  if ( collection->numGeometries() == 1 )
4424  {
4425  pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4426  }
4427  }
4428  }
4429 
4430  if ( ( fGeom1.type() != QgsWkbTypes::PointGeometry ) || ( fGeom2.type() != QgsWkbTypes::PointGeometry ) ||
4431  !pt1 || !pt2 )
4432  {
4433  parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
4434  return QVariant();
4435  }
4436 
4437  return pt1->inclination( *pt2 );
4438 
4439 }
4440 
4441 static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4442 {
4443  if ( values.length() != 3 )
4444  return QVariant();
4445 
4446  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4447  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4448  double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4449 
4450  QgsGeometry geom = fGeom.extrude( x, y );
4451 
4452  QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
4453  return result;
4454 }
4455 
4456 static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
4457 {
4458  if ( values.length() < 2 )
4459  return QVariant();
4460 
4461  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4462 
4463  if ( !fGeom.isMultipart() )
4464  return values.at( 0 );
4465 
4466  QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4467  QVariant cachedExpression;
4468  if ( ctx )
4469  cachedExpression = ctx->cachedValue( expString );
4470  QgsExpression expression;
4471 
4472  if ( cachedExpression.isValid() )
4473  {
4474  expression = cachedExpression.value<QgsExpression>();
4475  }
4476  else
4477  expression = QgsExpression( expString );
4478 
4479  bool asc = values.value( 2 ).toBool();
4480 
4481  QgsExpressionContext *unconstedContext = nullptr;
4482  QgsFeature f;
4483  if ( ctx )
4484  {
4485  // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
4486  // so no reason to worry
4487  unconstedContext = const_cast<QgsExpressionContext *>( ctx );
4488  f = ctx->feature();
4489  }
4490  else
4491  {
4492  // If there's no context provided, create a fake one
4493  unconstedContext = new QgsExpressionContext();
4494  }
4495 
4496  const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
4497  Q_ASSERT( collection ); // Should have failed the multipart check above
4498 
4500  orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
4501  QgsExpressionSorter sorter( orderBy );
4502 
4503  QList<QgsFeature> partFeatures;
4504  partFeatures.reserve( collection->partCount() );
4505  for ( int i = 0; i < collection->partCount(); ++i )
4506  {
4507  f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
4508  partFeatures << f;
4509  }
4510 
4511  sorter.sortFeatures( partFeatures, unconstedContext );
4512 
4513  QgsGeometryCollection *orderedGeom = qgsgeometry_cast<QgsGeometryCollection *>( fGeom.constGet()->clone() );
4514 
4515  Q_ASSERT( orderedGeom );
4516 
4517  while ( orderedGeom->partCount() )
4518  orderedGeom->removeGeometry( 0 );
4519 
4520  for ( const QgsFeature &feature : std::as_const( partFeatures ) )
4521  {
4522  orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
4523  }
4524 
4525  QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
4526 
4527  if ( !ctx )
4528  delete unconstedContext;
4529 
4530  return result;
4531 }
4532 
4533 static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4534 {
4535  QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4536  QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4537 
4538  QgsGeometry geom = fromGeom.nearestPoint( toGeom );
4539 
4540  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4541  return result;
4542 }
4543 
4544 static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4545 {
4546  QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4547  QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4548 
4549  QgsGeometry geom = fromGeom.shortestLine( toGeom );
4550 
4551  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4552  return result;
4553 }
4554 
4555 static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4556 {
4557  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4558  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4559 
4560  QgsGeometry geom = lineGeom.interpolate( distance );
4561 
4562  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4563  return result;
4564 }
4565 
4566 static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4567 {
4568  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4569  if ( lineGeom.type() != QgsWkbTypes::LineGeometry )
4570  {
4571  parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
4572  return QVariant();
4573  }
4574 
4575  const QgsCurve *curve = nullptr;
4576  if ( !lineGeom.isMultipart() )
4577  curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
4578  else
4579  {
4580  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
4581  {
4582  if ( collection->numGeometries() > 0 )
4583  {
4584  curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4585  }
4586  }
4587  }
4588  if ( !curve )
4589  return QVariant();
4590 
4591  double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4592  double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4593 
4594  std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
4595  QgsGeometry result( std::move( substring ) );
4596  return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
4597 }
4598 
4599 static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4600 {
4601  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4602  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4603 
4604  return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
4605 }
4606 
4607 static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4608 {
4609  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4610  int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4611  if ( vertex < 0 )
4612  {
4613  //negative idx
4614  int count = geom.constGet()->nCoordinates();
4615  vertex = count + vertex;
4616  }
4617 
4618  return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
4619 }
4620 
4621 static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4622 {
4623  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4624  int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4625  if ( vertex < 0 )
4626  {
4627  //negative idx
4628  int count = geom.constGet()->nCoordinates();
4629  vertex = count + vertex;
4630  }
4631 
4632  return geom.distanceToVertex( vertex );
4633 }
4634 
4635 static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4636 {
4637  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4638  QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4639 
4640  double distance = lineGeom.lineLocatePoint( pointGeom );
4641 
4642  return distance >= 0 ? distance : QVariant();
4643 }
4644 
4645 static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4646 {
4647  if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
4648  {
4649  double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4650  return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
4651  }
4652 
4653  if ( values.length() >= 1 )
4654  {
4655  double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4656  return QVariant( qlonglong( std::round( number ) ) );
4657  }
4658 
4659  return QVariant();
4660 }
4661 
4662 static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4663 {
4664  Q_UNUSED( values )
4665  Q_UNUSED( parent )
4666  return M_PI;
4667 }
4668 
4669 static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4670 {
4671  const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4672  const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4673  const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4674  if ( places < 0 )
4675  {
4676  parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
4677  return QVariant();
4678  }
4679 
4680  QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
4681  locale.setNumberOptions( locale.numberOptions() &= ~QLocale::NumberOption::OmitGroupSeparator );
4682  return locale.toString( value, 'f', places );
4683 }
4684 
4685 static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4686 {
4687  const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
4688  const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4689  const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4690 
4691  QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
4692  return locale.toString( datetime, format );
4693 }
4694 
4695 static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
4696 {
4697  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4698  int avg = ( color.red() + color.green() + color.blue() ) / 3;
4699  int alpha = color.alpha();
4700 
4701  color.setRgb( avg, avg, avg, alpha );
4702 
4703  return QgsSymbolLayerUtils::encodeColor( color );
4704 }
4705 
4706 static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4707 {
4708  QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4709  QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
4710  double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4711  if ( ratio > 1 )
4712  {
4713  ratio = 1;
4714  }
4715  else if ( ratio < 0 )
4716  {
4717  ratio = 0;
4718  }
4719 
4720  int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
4721  int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
4722  int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
4723  int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
4724 
4725  QColor newColor( red, green, blue, alpha );
4726 
4727  return QgsSymbolLayerUtils::encodeColor( newColor );
4728 }
4729 
4730 static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4731 {
4732  int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4733  int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4734  int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4735  QColor color = QColor( red, green, blue );
4736  if ( ! color.isValid() )
4737  {
4738  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
4739  color = QColor( 0, 0, 0 );
4740  }
4741 
4742  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
4743 }
4744 
4745 static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4746 {
4747  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
4748  QVariant value = node->eval( parent, context );
4749  if ( parent->hasEvalError() )
4750  {
4751  parent->setEvalErrorString( QString() );
4752  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
4754  value = node->eval( parent, context );
4756  }
4757  return value;
4758 }
4759 
4760 static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4761 {
4762  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
4764  QVariant value = node->eval( parent, context );
4766  if ( value.toBool() )
4767  {
4768  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
4770  value = node->eval( parent, context );
4772  }
4773  else
4774  {
4775  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
4777  value = node->eval( parent, context );
4779  }
4780  return value;
4781 }
4782 
4783 static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4784 {
4785  int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4786  int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4787  int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4788  int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
4789  QColor color = QColor( red, green, blue, alpha );
4790  if ( ! color.isValid() )
4791  {
4792  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
4793  color = QColor( 0, 0, 0 );
4794  }
4795  return QgsSymbolLayerUtils::encodeColor( color );
4796 }
4797 
4798 QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4799 {
4800  QgsGradientColorRamp expRamp;
4801  const QgsColorRamp *ramp = nullptr;
4802  if ( values.at( 0 ).canConvert<QgsGradientColorRamp>() )
4803  {
4804  expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
4805  ramp = &expRamp;
4806  }
4807  else
4808  {
4809  QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4810  ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
4811  if ( ! ramp )
4812  {
4813  parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
4814  return QVariant();
4815  }
4816  }
4817 
4818  double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4819  QColor color = ramp->color( value );
4820  return QgsSymbolLayerUtils::encodeColor( color );
4821 }
4822 
4823 static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4824 {
4825  // Hue ranges from 0 - 360
4826  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
4827  // Saturation ranges from 0 - 100
4828  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4829  // Lightness ranges from 0 - 100
4830  double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4831 
4832  QColor color = QColor::fromHslF( hue, saturation, lightness );
4833 
4834  if ( ! color.isValid() )
4835  {
4836  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
4837  color = QColor( 0, 0, 0 );
4838  }
4839 
4840  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
4841 }
4842 
4843 static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4844 {
4845  // Hue ranges from 0 - 360
4846  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
4847  // Saturation ranges from 0 - 100
4848  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4849  // Lightness ranges from 0 - 100
4850  double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4851  // Alpha ranges from 0 - 255
4852  double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
4853 
4854  QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
4855  if ( ! color.isValid() )
4856  {
4857  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
4858  color = QColor( 0, 0, 0 );
4859  }
4860  return QgsSymbolLayerUtils::encodeColor( color );
4861 }
4862 
4863 static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4864 {
4865  // Hue ranges from 0 - 360
4866  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
4867  // Saturation ranges from 0 - 100
4868  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4869  // Value ranges from 0 - 100
4870  double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4871 
4872  QColor color = QColor::fromHsvF( hue, saturation, value );
4873 
4874  if ( ! color.isValid() )
4875  {
4876  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
4877  color = QColor( 0, 0, 0 );
4878  }
4879 
4880  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
4881 }
4882 
4883 static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4884 {
4885  // Hue ranges from 0 - 360
4886  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
4887  // Saturation ranges from 0 - 100
4888  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4889  // Value ranges from 0 - 100
4890  double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4891  // Alpha ranges from 0 - 255
4892  double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
4893 
4894  QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
4895  if ( ! color.isValid() )
4896  {
4897  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
4898  color = QColor( 0, 0, 0 );
4899  }
4900  return QgsSymbolLayerUtils::encodeColor( color );
4901 }
4902 
4903 static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4904 {
4905  // Cyan ranges from 0 - 100
4906  double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
4907  // Magenta ranges from 0 - 100
4908  double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4909  // Yellow ranges from 0 - 100
4910  double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4911  // Black ranges from 0 - 100
4912  double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
4913 
4914  QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
4915 
4916  if ( ! color.isValid() )
4917  {
4918  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
4919  color = QColor( 0, 0, 0 );
4920  }
4921 
4922  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
4923 }
4924 
4925 static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4926 {
4927  // Cyan ranges from 0 - 100
4928  double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
4929  // Magenta ranges from 0 - 100
4930  double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4931  // Yellow ranges from 0 - 100
4932  double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4933  // Black ranges from 0 - 100
4934  double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
4935  // Alpha ranges from 0 - 255
4936  double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
4937 
4938  QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
4939  if ( ! color.isValid() )
4940  {
4941  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
4942  color = QColor( 0, 0, 0 );
4943  }
4944  return QgsSymbolLayerUtils::encodeColor( color );
4945 }
4946 
4947 static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4948 {
4949  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4950  if ( ! color.isValid() )
4951  {
4952  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
4953  return QVariant();
4954  }
4955 
4956  QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4957  if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
4958  return color.red();
4959  else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
4960  return color.green();
4961  else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
4962  return color.blue();
4963  else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
4964  return color.alpha();
4965  else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
4966  return color.hsvHueF() * 360;
4967  else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
4968  return color.hsvSaturationF() * 100;
4969  else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
4970  return color.valueF() * 100;
4971  else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
4972  return color.hslHueF() * 360;
4973  else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
4974  return color.hslSaturationF() * 100;
4975  else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
4976  return color.lightnessF() * 100;
4977  else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
4978  return color.cyanF() * 100;
4979  else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
4980  return color.magentaF() * 100;
4981  else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
4982  return color.yellowF() * 100;
4983  else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
4984  return color.blackF() * 100;
4985 
4986  parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
4987  return QVariant();
4988 }
4989 
4990 static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4991 {
4992  const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
4993  if ( map.count() < 1 )
4994  {
4995  parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
4996  return QVariant();
4997  }
4998 
4999  QList< QColor > colors;
5000  QgsGradientStopsList stops;
5001  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
5002  {
5003  colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
5004  if ( !colors.last().isValid() )
5005  {
5006  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
5007  return QVariant();
5008  }
5009 
5010  double step = it.key().toDouble();
5011  if ( it == map.constBegin() )
5012  {
5013  if ( step != 0.0 )
5014  stops << QgsGradientStop( step, colors.last() );
5015  }
5016  else if ( it == map.constEnd() )
5017  {
5018  if ( step != 1.0 )
5019  stops << QgsGradientStop( step, colors.last() );
5020  }
5021  else
5022  {
5023  stops << QgsGradientStop( step, colors.last() );
5024  }
5025  }
5026  bool discrete = values.at( 1 ).toBool();
5027 
5028  return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
5029 }
5030 
5031 static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5032 {
5033  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5034  if ( ! color.isValid() )
5035  {
5036  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5037  return QVariant();
5038  }
5039 
5040  QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5041  int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5042  if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
5043  color.setRed( value );
5044  else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
5045  color.setGreen( value );
5046  else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
5047  color.setBlue( value );
5048  else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
5049  color.setAlpha( value );
5050  else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
5051  color.setHsv( value, color.hsvSaturation(), color.value(), color.alpha() );
5052  else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
5053  color.setHsvF( color.hsvHueF(), value / 100.0, color.valueF(), color.alphaF() );
5054  else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
5055  color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), value / 100.0, color.alphaF() );
5056  else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
5057  color.setHsl( value, color.hslSaturation(), color.lightness(), color.alpha() );
5058  else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
5059  color.setHslF( color.hslHueF(), value / 100.0, color.lightnessF(), color.alphaF() );
5060  else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
5061  color.setHslF( color.hslHueF(), color.hslSaturationF(), value / 100.0, color.alphaF() );
5062  else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
5063  color.setCmykF( value / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
5064  else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
5065  color.setCmykF( color.cyanF(), value / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
5066  else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
5067  color.setCmykF( color.cyanF(), color.magentaF(), value / 100.0, color.blackF(), color.alphaF() );
5068  else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
5069  color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), value / 100.0, color.alphaF() );
5070  else
5071  {
5072  parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
5073  return QVariant();
5074  }
5075  return QgsSymbolLayerUtils::encodeColor( color );
5076 }
5077 
5078 static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5079 {
5080  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5081  if ( ! color.isValid() )
5082  {
5083  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5084  return QVariant();
5085  }
5086 
5087  color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5088 
5089  return QgsSymbolLayerUtils::encodeColor( color );
5090 }
5091 
5092 static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5093 {
5094  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5095  if ( ! color.isValid() )
5096  {
5097  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5098  return QVariant();
5099  }
5100 
5101  color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5102 
5103  return QgsSymbolLayerUtils::encodeColor( color );
5104 }
5105 
5106 static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5107 {
5108  QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
5109  QgsGeometry geom = feat.geometry();
5110  if ( !geom.isNull() )
5111  return QVariant::fromValue( geom );
5112  return QVariant();
5113 }
5114 
5115 static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5116 {
5117  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5118  QString sAuthId = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5119  QString dAuthId = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5120 
5122  if ( ! s.isValid() )
5123  return QVariant::fromValue( fGeom );
5125  if ( ! d.isValid() )
5126  return QVariant::fromValue( fGeom );
5127 
5129  if ( context )
5130  tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5131  QgsCoordinateTransform t( s, d, tContext );
5132  try
5133  {
5135  return QVariant::fromValue( fGeom );
5136  }
5137  catch ( QgsCsException &cse )
5138  {
5139  QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
5140  return QVariant();
5141  }
5142  return QVariant();
5143 }
5144 
5145 
5146 static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5147 {
5148  QVariant result;
5149  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
5150  if ( vl )
5151  {
5152  QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
5153 
5154  QgsFeatureRequest req;
5155  req.setFilterFid( fid );
5156  req.setTimeout( 10000 );
5157  req.setRequestMayBeNested( true );
5158  if ( context )
5159  req.setFeedback( context->feedback() );
5160  QgsFeatureIterator fIt = vl->getFeatures( req );
5161 
5162  QgsFeature fet;
5163  if ( fIt.nextFeature( fet ) )
5164  result = QVariant::fromValue( fet );
5165  }
5166 
5167  return result;
5168 }
5169 
5170 static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5171 {
5172  //arguments: 1. layer id / name, 2. key attribute, 3. eq value
5173 
5174  std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), parent );
5175 
5176  //no layer found
5177  if ( !featureSource )
5178  {
5179  return QVariant();
5180  }
5181 
5182  QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5183  int attributeId = featureSource->fields().lookupField( attribute );
5184  if ( attributeId == -1 )
5185  {
5186  return QVariant();
5187  }
5188 
5189  const QVariant &attVal = values.at( 2 );
5190 
5191  const QString cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
5192  if ( context && context->hasCachedValue( cacheValueKey ) )
5193  {
5194  return context->cachedValue( cacheValueKey );
5195  }
5196 
5197  QgsFeatureRequest req;
5198  req.setFilterExpression( QStringLiteral( "%1=%2" ).arg( QgsExpression::quotedColumnRef( attribute ),
5199  QgsExpression::quotedString( attVal.toString() ) ) );
5200  req.setLimit( 1 );
5201  req.setTimeout( 10000 );
5202  req.setRequestMayBeNested( true );
5203  if ( context )
5204  req.setFeedback( context->feedback() );
5205  if ( !parent->needsGeometry() )
5206  {
5208  }
5209  QgsFeatureIterator fIt = featureSource->getFeatures( req );
5210 
5211  QgsFeature fet;
5212  QVariant res;
5213  if ( fIt.nextFeature( fet ) )
5214  {
5215  res = QVariant::fromValue( fet );
5216  }
5217 
5218  if ( context )
5219  context->setCachedValue( cacheValueKey, res );
5220  return res;
5221 }
5222 
5223 static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
5224 {
5225  QVariant result;
5226  QString fieldName;
5227 
5228  if ( context )
5229  {
5230  if ( !values.isEmpty() )
5231  {
5232  QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
5233  if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
5234  fieldName = col->name();
5235  else if ( values.size() == 2 )
5236  fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5237  }
5238 
5239  QVariant value = values.at( 0 );
5240 
5241  const QgsFields fields = context->fields();
5242  int fieldIndex = fields.lookupField( fieldName );
5243 
5244  if ( fieldIndex == -1 )
5245  {
5246  parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
5247  }
5248  else
5249  {
5250  QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
5251 
5252  const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
5253  if ( context->hasCachedValue( cacheValueKey ) )
5254  {
5255  return context->cachedValue( cacheValueKey );
5256  }
5257 
5258  const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
5260 
5261  const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
5262 
5263  QVariant cache;
5264  if ( !context->hasCachedValue( cacheKey ) )
5265  {
5266  cache = formatter->createCache( layer, fieldIndex, setup.config() );
5267  context->setCachedValue( cacheKey, cache );
5268  }
5269  else
5270  cache = context->cachedValue( cacheKey );
5271 
5272  result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
5273 
5274  context->setCachedValue( cacheValueKey, result );
5275  }
5276  }
5277  else
5278  {
5279  parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
5280  }
5281 
5282  return result;
5283 }
5284 
5285 static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5286 {
5287  const QVariant data = values.at( 0 );
5288  const QMimeDatabase db;
5289  return db.mimeTypeForData( data.toByteArray() ).name();
5290 }
5291 
5292 static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5293 {
5294  QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
5295 
5296  if ( !layer )
5297  return QVariant();
5298 
5299  // here, we always prefer the layer metadata values over the older server-specific published values
5300  QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5301  if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
5302  return layer->name();
5303  else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
5304  return layer->id();
5305  else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
5306  return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->title();
5307  else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
5308  return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->abstract();
5309  else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
5310  {
5311  QStringList keywords;
5312  const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
5313  for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
5314  {
5315  keywords.append( it.value() );
5316  }
5317  if ( !keywords.isEmpty() )
5318  return keywords;
5319  return layer->keywordList();
5320  }
5321  else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
5322  return layer->dataUrl();
5323  else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
5324  {
5325  return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->attribution() );
5326  }
5327  else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
5328  return layer->attributionUrl();
5329  else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
5330  return layer->publicSource();
5331  else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
5332  return layer->minimumScale();
5333  else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
5334  return layer->maximumScale();
5335  else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
5336  return layer->isEditable();
5337  else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
5338  return layer->crs().authid();
5339  else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
5340  return layer->crs().toProj();
5341  else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
5342  return layer->crs().description();
5343  else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
5344  {
5345  QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
5346  QVariant result = QVariant::fromValue( extentGeom );
5347  return result;
5348  }
5349  else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
5350  return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
5351  else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
5352  {
5353  switch ( layer->type() )
5354  {
5356  return QCoreApplication::translate( "expressions", "Vector" );
5358  return QCoreApplication::translate( "expressions", "Raster" );
5360  return QCoreApplication::translate( "expressions", "Mesh" );
5362  return QCoreApplication::translate( "expressions", "Vector Tile" );
5364  return QCoreApplication::translate( "expressions", "Plugin" );
5366  return QCoreApplication::translate( "expressions", "Annotation" );
5368  return QCoreApplication::translate( "expressions", "Point Cloud" );
5369  }
5370  }
5371  else
5372  {
5373  //vector layer methods
5374  QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
5375  if ( vLayer )
5376  {
5377  if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
5378  return vLayer->storageType();
5379  else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
5381  else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
5382  return QVariant::fromValue( vLayer->featureCount() );
5383  else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
5384  {
5385  if ( vLayer->dataProvider() )
5386  {
5387  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
5388  return decodedUri.value( QStringLiteral( "path" ) );
5389  }
5390  }
5391  }
5392  }
5393 
5394  return QVariant();
5395 }
5396 
5397 static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5398 {
5399  QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
5400  if ( !layer )
5401  {
5402  parent->setEvalErrorString( QObject::tr( "Cannot find layer %1" ).arg( values.at( 0 ).toString() ) );
5403  return QVariant();
5404  }
5405 
5406  if ( !layer->dataProvider() )
5407  {
5408  parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
5409  return QVariant();
5410  }
5411 
5412  const QString uriPart = values.at( 1 ).toString();
5413 
5414  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
5415 
5416  if ( !uriPart.isNull() )
5417  {
5418  return decodedUri.value( values.at( 1 ).toString() );
5419  }
5420  else
5421  {
5422  return decodedUri;
5423  }
5424 }
5425 
5426 static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5427 {
5428  QString layerIdOrName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5429 
5430  //try to find a matching layer by name
5431  QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerIdOrName ); //search by id first
5432  if ( !layer )
5433  {
5434  QList<QgsMapLayer *> layersByName = QgsProject::instance()->mapLayersByName( layerIdOrName );
5435  if ( !layersByName.isEmpty() )
5436  {
5437  layer = layersByName.at( 0 );
5438  }
5439  }
5440 
5441  if ( !layer )
5442  return QVariant();
5443 
5444  QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
5445  if ( !rl )
5446  return QVariant();
5447 
5448  int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5449  if ( band < 1 || band > rl->bandCount() )
5450  {
5451  parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer %2" ).arg( band ).arg( layerIdOrName ) );
5452  return QVariant();
5453  }
5454 
5455  QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5456  int stat = 0;
5457 
5458  if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
5459  stat = QgsRasterBandStats::Mean;
5460  else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
5462  else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
5463  stat = QgsRasterBandStats::Min;
5464  else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
5465  stat = QgsRasterBandStats::Max;
5466  else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
5468  else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
5469  stat = QgsRasterBandStats::Sum;
5470  else
5471  {
5472  parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
5473  return QVariant();
5474  }
5475 
5476  QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
5477  switch ( stat )
5478  {
5480  return stats.mean;
5482  return stats.stdDev;
5484  return stats.minimumValue;
5486  return stats.maximumValue;
5488  return stats.range;
5490  return stats.sum;
5491  }
5492  return QVariant();
5493 }
5494 
5495 static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5496 {
5497  return values;
5498 }
5499 
5500 static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5501 {
5502  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5503  bool ascending = values.value( 1 ).toBool();
5504  std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
5505  return list;
5506 }
5507 
5508 static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5509 {
5510  return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
5511 }
5512 
5513 static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5514 {
5515  return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
5516 }
5517 
5518 static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5519 {
5520  return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
5521 }
5522 
5523 static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5524 {
5525  QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5526  QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
5527  int match = 0;
5528  for ( const auto &item : listB )
5529  {
5530  if ( listA.contains( item ) )
5531  match++;
5532  }
5533 
5534  return QVariant( match == listB.count() );
5535 }
5536 
5537 static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5538 {
5539  return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
5540 }
5541 
5542 static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5543 {
5544  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5545  const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5546  if ( pos < list.length() && pos >= 0 ) return list.at( pos );
5547  else if ( pos < 0 && ( list.length() + pos ) >= 0 )
5548  return list.at( list.length() + pos );
5549  return QVariant();
5550 }
5551 
5552 static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5553 {
5554  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5555  return list.value( 0 );
5556 }
5557 
5558 static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5559 {
5560  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5561  return list.value( list.size() - 1 );
5562 }
5563 
5564 static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5565 {
5566  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5567  return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
5568 }
5569 
5570 static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5571 {
5572  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5573  return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
5574 }
5575 
5576 static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5577 {
5578  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5579  int i = 0;
5580  double total = 0.0;
5581  for ( const QVariant &item : list )
5582  {
5583  switch ( item.userType() )
5584  {
5585  case QMetaType::Int:
5586  case QMetaType::UInt:
5587  case QMetaType::LongLong:
5588  case QMetaType::ULongLong:
5589  case QMetaType::Float:
5590  case QMetaType::Double:
5591  total += item.toDouble();
5592  ++i;
5593  break;
5594  }
5595  }
5596  return i == 0 ? QVariant() : total / i;
5597 }
5598 
5599 static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5600 {
5601  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5602  QVariantList numbers;
5603  for ( const auto &item : list )
5604  {
5605  switch ( item.userType() )
5606  {
5607  case QMetaType::Int:
5608  case QMetaType::UInt:
5609  case QMetaType::LongLong:
5610  case QMetaType::ULongLong:
5611  case QMetaType::Float:
5612  case QMetaType::Double:
5613  numbers.append( item );
5614  break;
5615  }
5616  }
5617  std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
5618  const int count = numbers.count();
5619  if ( count == 0 )
5620  {
5621  return QVariant();
5622  }
5623  else if ( count % 2 )
5624  {
5625  return numbers.at( count / 2 );
5626  }
5627  else
5628  {
5629  return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
5630  }
5631 }
5632 
5633 static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5634 {
5635  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5636  int i = 0;
5637  double total = 0.0;
5638  for ( const QVariant &item : list )
5639  {
5640  switch ( item.userType() )
5641  {
5642  case QMetaType::Int:
5643  case QMetaType::UInt:
5644  case QMetaType::LongLong:
5645  case QMetaType::ULongLong:
5646  case QMetaType::Float:
5647  case QMetaType::Double:
5648  total += item.toDouble();
5649  ++i;
5650  break;
5651  }
5652  }
5653  return i == 0 ? QVariant() : total;
5654 }
5655 
5656 static QVariant convertToSameType( const QVariant &value, QVariant::Type type )
5657 {
5658  QVariant result = value;
5659  result.convert( static_cast<int>( type ) );
5660  return result;
5661 }
5662 
5663 static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
5664 {
5665  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5666  QHash< QVariant, int > hash;
5667  for ( const auto &item : list )
5668  {
5669  ++hash[item];
5670  }
5671  const QList< int > occurrences = hash.values();
5672  if ( occurrences.empty() )
5673  return QVariantList();
5674 
5675  const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
5676 
5677  const QString option = values.at( 1 ).toString();
5678  if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
5679  {
5680  return convertToSameType( hash.keys( maxValue ), values.at( 0 ).type() );
5681  }
5682  else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
5683  {
5684  if ( hash.isEmpty() )
5685  return QVariant();
5686 
5687  return QVariant( hash.keys( maxValue ).first() );
5688  }
5689  else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
5690  {
5691  return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), values.at( 0 ).type() ), context, parent, node );
5692  }
5693  else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
5694  {
5695  if ( maxValue * 2 <= list.size() )
5696  return QVariant();
5697 
5698  return QVariant( hash.keys( maxValue ).first() );
5699  }
5700  else
5701  {
5702  parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
5703  return QVariant();
5704  }
5705 }
5706 
5707 static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
5708 {
5709  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5710  QHash< QVariant, int > hash;
5711  for ( const auto &item : list )
5712  {
5713  ++hash[item];
5714  }
5715  const QList< int > occurrences = hash.values();
5716  if ( occurrences.empty() )
5717  return QVariantList();
5718 
5719  const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
5720 
5721  const QString option = values.at( 1 ).toString();
5722  if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
5723  {
5724  return convertToSameType( hash.keys( minValue ), values.at( 0 ).type() );
5725  }
5726  else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
5727  {
5728  if ( hash.isEmpty() )
5729  return QVariant();
5730 
5731  return QVariant( hash.keys( minValue ).first() );
5732  }
5733  else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
5734  {
5735  return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), values.at( 0 ).type() ), context, parent, node );
5736  }
5737  else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
5738  {
5739  if ( hash.keys().isEmpty() )
5740  return QVariant();
5741 
5742  // Remove the majority, all others are minority
5743  const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
5744  if ( maxValue * 2 > list.size() )
5745  hash.remove( hash.key( maxValue ) );
5746 
5747  return convertToSameType( hash.keys(), values.at( 0 ).type() );
5748  }
5749  else
5750  {
5751  parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
5752  return QVariant();
5753  }
5754 }
5755 
5756 static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5757 {
5758  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5759  list.append( values.at( 1 ) );
5760  return convertToSameType( list, values.at( 0 ).type() );
5761 }
5762 
5763 static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5764 {
5765  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5766  list.prepend( values.at( 1 ) );
5767  return convertToSameType( list, values.at( 0 ).type() );
5768 }
5769 
5770 static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5771 {
5772  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5773  list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
5774  return convertToSameType( list, values.at( 0 ).type() );
5775 }
5776 
5777 static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5778 {
5779  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5780  list.removeAt( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5781  return convertToSameType( list, values.at( 0 ).type() );
5782 }
5783 
5784 static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5785 {
5786  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5787  list.removeAll( values.at( 1 ) );
5788  return convertToSameType( list, values.at( 0 ).type() );
5789 }
5790 
5791 static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5792 {
5793  if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
5794  {
5795  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
5796 
5797  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5798  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
5799  {
5800  int index = list.indexOf( it.key() );
5801  while ( index >= 0 )
5802  {
5803  list.replace( index, it.value() );
5804  index = list.indexOf( it.key() );
5805  }
5806  }
5807 
5808  return convertToSameType( list, values.at( 0 ).type() );
5809  }
5810  else if ( values.count() == 3 )
5811  {
5812  QVariantList before;
5813  QVariantList after;
5814  bool isSingleReplacement = false;
5815 
5816  if ( values.at( 1 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
5817  {
5818  before = QVariantList() << values.at( 1 );
5819  }
5820  else
5821  {
5822  before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
5823  }
5824 
5825  if ( values.at( 2 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
5826  {
5827  after = QVariantList() << values.at( 2 );
5828  isSingleReplacement = true;
5829  }
5830  else
5831  {
5832  after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
5833  }
5834 
5835  if ( !isSingleReplacement && before.length() != after.length() )
5836  {
5837  parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
5838  return QVariant();
5839  }
5840 
5841  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5842  for ( int i = 0; i < before.length(); i++ )
5843  {
5844  int index = list.indexOf( before.at( i ) );
5845  while ( index >= 0 )
5846  {
5847  list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
5848  index = list.indexOf( before.at( i ) );
5849  }
5850  }
5851 
5852  return convertToSameType( list, values.at( 0 ).type() );
5853  }
5854  else
5855  {
5856  parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
5857  return QVariant();
5858  }
5859 }
5860 
5861 static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5862 {
5863  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5864  QVariantList list_new;
5865 
5866  for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
5867  {
5868  while ( list.removeOne( cur ) )
5869  {
5870  list_new.append( cur );
5871  }
5872  }
5873 
5874  list_new.append( list );
5875 
5876  return convertToSameType( list_new, values.at( 0 ).type() );
5877 }
5878 
5879 static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5880 {
5881  QVariantList list;
5882  for ( const QVariant &cur : values )
5883  {
5884  list += QgsExpressionUtils::getListValue( cur, parent );
5885  }
5886  return convertToSameType( list, values.at( 0 ).type() );
5887 }
5888 
5889 static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5890 {
5891  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5892  int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5893  const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5894  int slice_length = 0;
5895  // negative positions means positions taken relative to the end of the array
5896  if ( start_pos < 0 )
5897  {
5898  start_pos = list.length() + start_pos;
5899  }
5900  if ( end_pos >= 0 )
5901  {
5902  slice_length = end_pos - start_pos + 1;
5903  }
5904  else
5905  {
5906  slice_length = list.length() + end_pos - start_pos + 1;
5907  }
5908  //avoid negative lengths in QList.mid function
5909  if ( slice_length < 0 )
5910  {
5911  slice_length = 0;
5912  }
5913  list = list.mid( start_pos, slice_length );
5914  return list;
5915 }
5916 
5917 static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5918 {
5919  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5920  std::reverse( list.begin(), list.end() );
5921  return list;
5922 }
5923 
5924 static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5925 {
5926  const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5927  const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
5928  for ( const QVariant &cur : array2 )
5929  {
5930  if ( array1.contains( cur ) )
5931  return QVariant( true );
5932  }
5933  return QVariant( false );
5934 }
5935 
5936 static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5937 {
5938  QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5939 
5940  QVariantList distinct;
5941 
5942  for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
5943  {
5944  if ( !distinct.contains( *it ) )
5945  {
5946  distinct += ( *it );
5947  }
5948  }
5949 
5950  return distinct;
5951 }
5952 
5953 static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5954 {
5955  QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5956  QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5957  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5958 
5959  QString str;
5960 
5961  for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
5962  {
5963  str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
5964  if ( it != ( array.constEnd() - 1 ) )
5965  {
5966  str += delimiter;
5967  }
5968  }
5969 
5970  return QVariant( str );
5971 }
5972 
5973 static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5974 {
5975  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5976  QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5977  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5978 
5979  QStringList list = str.split( delimiter );
5980  QVariantList array;
5981 
5982  for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
5983  {
5984  array += ( !( *it ).isEmpty() ) ? *it : empty;
5985  }
5986 
5987  return array;
5988 }
5989 
5990 static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5991 {
5992  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5993  QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
5994  if ( document.isNull() )
5995  return QVariant();
5996 
5997  return document.toVariant();
5998 }
5999 
6000 static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6001 {
6002  Q_UNUSED( parent )
6003  QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
6004  return document.toJson( QJsonDocument::Compact );
6005 }
6006 
6007 static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6008 {
6009  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6010  if ( str.isEmpty() )
6011  return QVariantMap();
6012  str = str.trimmed();
6013 
6014  return QgsHstoreUtils::parse( str );
6015 }
6016 
6017 static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6018 {
6019  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6020  return QgsHstoreUtils::build( map );
6021 }
6022 
6023 static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6024 {
6025  QVariantMap result;
6026  for ( int i = 0; i + 1 < values.length(); i += 2 )
6027  {
6028  result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
6029  }
6030  return result;
6031 }
6032 
6033 static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6034 {
6035  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
6036 }
6037 
6038 static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6039 {
6040  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
6041 }
6042 
6043 static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6044 {
6045  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6046  map.remove( values.at( 1 ).toString() );
6047  return map;
6048 }
6049 
6050 static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6051 {
6052  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6053  map.insert( values.at( 1 ).toString(), values.at( 2 ) );
6054  return map;
6055 }
6056 
6057 static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6058 {
6059  QVariantMap result;
6060  for ( const QVariant &cur : values )
6061  {
6062  const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
6063  for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
6064  result.insert( it.key(), it.value() );
6065  }
6066  return result;
6067 }
6068 
6069 static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6070 {
6071  return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
6072 }
6073 
6074 static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6075 {
6076  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
6077 }
6078 
6079 static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6080 {
6081  QString envVarName = values.at( 0 ).toString();
6082  return QProcessEnvironment::systemEnvironment().value( envVarName );
6083 }
6084 
6085 static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6086 {
6087  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6088  return QFileInfo( file ).completeBaseName();
6089 }
6090 
6091 static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6092 {
6093  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6094  return QFileInfo( file ).completeSuffix();
6095 }
6096 
6097 static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6098 {
6099  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6100  return QFileInfo::exists( file );
6101 }
6102 
6103 static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6104 {
6105  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6106  return QFileInfo( file ).fileName();
6107 }
6108 
6109 static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6110 {
6111  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6112  return QFileInfo( file ).isFile();
6113 }
6114 
6115 static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6116 {
6117  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6118  return QFileInfo( file ).isDir();
6119 }
6120 
6121 static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6122 {
6123  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6124  return QDir::toNativeSeparators( QFileInfo( file ).path() );
6125 }
6126 
6127 static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6128 {
6129  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6130  return QFileInfo( file ).size();
6131 }
6132 
6133 static QVariant fcnHash( const QString str, const QCryptographicHash::Algorithm algorithm )
6134 {
6135  return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
6136 }
6137 
6138 static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6139 {
6140  QVariant hash;
6141  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6142  QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
6143 
6144  if ( method == QLatin1String( "md4" ) )
6145  {
6146  hash = fcnHash( str, QCryptographicHash::Md4 );
6147  }
6148  else if ( method == QLatin1String( "md5" ) )
6149  {
6150  hash = fcnHash( str, QCryptographicHash::Md5 );
6151  }
6152  else if ( method == QLatin1String( "sha1" ) )
6153  {
6154  hash = fcnHash( str, QCryptographicHash::Sha1 );
6155  }
6156  else if ( method == QLatin1String( "sha224" ) )
6157  {
6158  hash = fcnHash( str, QCryptographicHash::Sha224 );
6159  }
6160  else if ( method == QLatin1String( "sha256" ) )
6161  {
6162  hash = fcnHash( str, QCryptographicHash::Sha256 );
6163  }
6164  else if ( method == QLatin1String( "sha384" ) )
6165  {
6166  hash = fcnHash( str, QCryptographicHash::Sha384 );
6167  }
6168  else if ( method == QLatin1String( "sha512" ) )
6169  {
6170  hash = fcnHash( str, QCryptographicHash::Sha512 );
6171  }
6172  else if ( method == QLatin1String( "sha3_224" ) )
6173  {
6174  hash = fcnHash( str, QCryptographicHash::Sha3_224 );
6175  }
6176  else if ( method == QLatin1String( "sha3_256" ) )
6177  {
6178  hash = fcnHash( str, QCryptographicHash::Sha3_256 );
6179  }
6180  else if ( method == QLatin1String( "sha3_384" ) )
6181  {
6182  hash = fcnHash( str, QCryptographicHash::Sha3_384 );
6183  }
6184  else if ( method == QLatin1String( "sha3_512" ) )
6185  {
6186  hash = fcnHash( str, QCryptographicHash::Sha3_512 );
6187  }
6188  else if ( method == QLatin1String( "keccak_224" ) )
6189  {
6190  hash = fcnHash( str, QCryptographicHash::Keccak_224 );
6191  }
6192  else if ( method == QLatin1String( "keccak_256" ) )
6193  {
6194  hash = fcnHash( str, QCryptographicHash::Keccak_256 );
6195  }
6196  else if ( method == QLatin1String( "keccak_384" ) )
6197  {
6198  hash = fcnHash( str, QCryptographicHash::Keccak_384 );
6199  }
6200  else if ( method == QLatin1String( "keccak_512" ) )
6201  {
6202  hash = fcnHash( str, QCryptographicHash::Keccak_512 );
6203  }
6204  else
6205  {
6206  parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
6207  }
6208  return hash;
6209 }
6210 
6211 static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6212 {
6213  return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
6214 }
6215 
6216 static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6217 {
6218  return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
6219 }
6220 
6221 static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6222 {
6223  const QByteArray input = values.at( 0 ).toByteArray();
6224  return QVariant( QString( input.toBase64() ) );
6225 }
6226 
6227 static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6228 {
6229  const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6230  const QByteArray base64 = value.toLocal8Bit();
6231  const QByteArray decoded = QByteArray::fromBase64( base64 );
6232  return QVariant( decoded );
6233 }
6234 
6235 typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
6236 
6237 static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const RelationFunction &relationFunction, bool invert = false, double bboxGrow = 0, bool isNearestFunc = false )
6238 {
6239 
6240  const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
6241  QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, parent );
6242 
6243  QgsFeatureRequest request;
6244  request.setTimeout( 10000 );
6245  request.setRequestMayBeNested( true );
6246  request.setFeedback( context->feedback() );
6247 
6248  // First parameter is the overlay layer
6249  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6251 
6252  const bool layerCanBeCached = node->isStatic( parent, context );
6253  QVariant targetLayerValue = node->eval( parent, context );
6255 
6256  // Second parameter is the expression to evaluate (or null for testonly)
6257  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6259  QString subExpString = node->dump();
6260 
6261  bool testOnly = ( subExpString == "NULL" );
6262  QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, parent );
6263  if ( !targetLayer ) // No layer, no joy
6264  {
6265  parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
6266  return QVariant();
6267  }
6268 
6269  // Third parameter is the filtering expression
6270  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6272  QString filterString = node->dump();
6273  if ( filterString != "NULL" )
6274  {
6275  request.setFilterExpression( filterString ); //filter cached features
6276  }
6277 
6278  // Fourth parameter is the limit
6279  node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
6281  QVariant limitValue = node->eval( parent, context );
6283  qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
6284 
6285  // Fifth parameter (for nearest only) is the max distance
6286  double max_distance = 0;
6287  if ( isNearestFunc ) //maxdistance param handling
6288  {
6289  node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
6291  QVariant distanceValue = node->eval( parent, context );
6293  max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
6294  }
6295 
6296  // Fifth or sixth (for nearest only) parameter is the cache toggle
6297  node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
6299  QVariant cacheValue = node->eval( parent, context );
6301  bool cacheEnabled = cacheValue.toBool();
6302 
6303 
6304  FEAT_FROM_CONTEXT( context, feat )
6305  const QgsGeometry geometry = feat.geometry();
6306 
6307  if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
6308  {
6309  QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
6310  request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
6311  }
6312 
6313  bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
6314 
6315  QgsRectangle intDomain = geometry.boundingBox();
6316  if ( bboxGrow != 0 )
6317  {
6318  intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
6319  }
6320 
6321  const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
6322 
6323  // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
6324  // Otherwise, it can be toggled by the user
6325  QgsSpatialIndex spatialIndex;
6326  QgsVectorLayer *cachedTarget;
6327  QList<QgsFeature> features;
6328  if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
6329  {
6330  // If the cache (local spatial index) is enabled, we materialize the whole
6331  // layer, then do the request on that layer instead.
6332  const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
6333  const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
6334 
6335  if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
6336  {
6337  cachedTarget = targetLayer->materialize( request );
6338  if ( layerCanBeCached )
6339  context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
6340  }
6341  else
6342  {
6343  cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
6344  }
6345 
6346  if ( !context->hasCachedValue( cacheIndex ) )
6347  {
6348  spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
6349  if ( layerCanBeCached )
6350  context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
6351  }
6352  else
6353  {
6354  spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
6355  }
6356 
6357  QList<QgsFeatureId> fidsList;
6358  if ( isNearestFunc )
6359  {
6360  fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
6361  }
6362  else
6363  {
6364  fidsList = spatialIndex.intersects( intDomain );
6365  }
6366 
6367  QListIterator<QgsFeatureId> i( fidsList );
6368  while ( i.hasNext() )
6369  {
6370  QgsFeatureId fId2 = i.next();
6371  if ( sameLayers && feat.id() == fId2 )
6372  continue;
6373  features.append( cachedTarget->getFeature( fId2 ) );
6374  }
6375 
6376  }
6377  else
6378  {
6379  // If the cache (local spatial index) is not enabled, we directly
6380  // get the features from the target layer
6381  request.setFilterRect( intDomain );
6382  QgsFeatureIterator fit = targetLayer->getFeatures( request );
6383  QgsFeature feat2;
6384  while ( fit.nextFeature( feat2 ) )
6385  {
6386  if ( sameLayers && feat.id() == feat2.id() )
6387  continue;
6388  features.append( feat2 );
6389  }
6390  }
6391 
6392  QgsExpression subExpression;
6393  QgsExpressionContext subContext;
6394  if ( !testOnly )
6395  {
6396  const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
6397  const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
6398 
6399  if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
6400  {
6401  subExpression = QgsExpression( subExpString );
6403  subExpression.prepare( &subContext );
6404  }
6405  else
6406  {
6407  subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
6408  subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
6409  }
6410  }
6411 
6412 
6413  bool found = false;
6414  int foundCount = 0;
6415  QVariantList results;
6416 
6417  QListIterator<QgsFeature> i( features );
6418  while ( i.hasNext() && ( limit == -1 || foundCount < limit ) )
6419  {
6420  QgsFeature feat2 = i.next();
6421 
6422  if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
6423  {
6424  found = true;
6425  foundCount++;
6426 
6427  // We just want a single boolean result if there is any intersect: finish and return true
6428  if ( testOnly )
6429  break;
6430 
6431  if ( !invert )
6432  {
6433  // We want a list of attributes / geometries / other expression values, evaluate now
6434  subContext.setFeature( feat2 );
6435  results.append( subExpression.evaluate( &subContext ) );
6436  }
6437  else
6438  {
6439  // If not, results is a list of found ids, which we'll inverse and evaluate below
6440  results.append( feat2.id() );
6441  }
6442  }
6443  }
6444 
6445  if ( testOnly )
6446  {
6447  if ( invert )
6448  found = !found;//for disjoint condition
6449  return found;
6450  }
6451 
6452  if ( !invert )
6453  return results;
6454 
6455  // for disjoint condition returns the results for cached layers not intersected feats
6456  QVariantList disjoint_results;
6457  QgsFeature feat2;
6458  QgsFeatureRequest request2;
6459  request2.setLimit( limit );
6460  if ( context )
6461  request2.setFeedback( context->feedback() );
6462  QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
6463  while ( fi.nextFeature( feat2 ) )
6464  {
6465  if ( !results.contains( feat2.id() ) )
6466  {
6467  subContext.setFeature( feat2 );
6468  disjoint_results.append( subExpression.evaluate( &subContext ) );
6469  }
6470  }
6471  return disjoint_results;
6472 
6473 }
6474 
6475 // Intersect functions:
6476 
6477 static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6478 {
6479  return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects );
6480 }
6481 
6482 static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6483 {
6484  return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
6485 }
6486 
6487 static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6488 {
6489  return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
6490 }
6491 
6492 static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6493 {
6494  return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
6495 }
6496 
6497 static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6498 {
6499  return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
6500 }
6501 
6502 static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6503 {
6504  return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
6505 }
6506 
6507 static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6508 {
6509  return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true );
6510 }
6511 
6512 static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6513 {
6514  return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
6515 }
6516 
6517 const QList<QgsExpressionFunction *> &QgsExpression::Functions()
6518 {
6519  // The construction of the list isn't thread-safe, and without the mutex,
6520  // crashes in the WFS provider may occur, since it can parse expressions
6521  // in parallel.
6522  // The mutex needs to be recursive.
6523 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
6524  static QMutex sFunctionsMutex( QMutex::Recursive );
6525  QMutexLocker locker( &sFunctionsMutex );
6526 #else
6527  static QRecursiveMutex sFunctionsMutex;
6528  QMutexLocker locker( &sFunctionsMutex );
6529 #endif
6530 
6531  QList<QgsExpressionFunction *> &functions = *sFunctions();
6532 
6533  if ( functions.isEmpty() )
6534  {
6536  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
6537  << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
6538  << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
6539 
6540  QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
6541  aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
6542  << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
6543 
6544  QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
6545  aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
6546 
6547  functions
6548  << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
6549  << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
6550  << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
6551  << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
6552  << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
6553  << 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" ) )
6554  << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
6555  << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
6556  << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
6557  << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
6558  << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
6559  << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
6560  << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
6561  << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
6562  << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
6563  << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
6564  << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
6565  << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
6566  << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
6567 
6568  QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
6569  randFunc->setIsStatic( false );
6570  functions << randFunc;
6571 
6572  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" ) );
6573  randfFunc->setIsStatic( false );
6574  functions << randfFunc;
6575 
6576  functions
6577  << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
6578  << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
6579  << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
6580  << new QgsStaticExpressionFunction( QStringLiteral( "scale_linear" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ), fcnLinearScale, QStringLiteral( "Math" ) )
6581  << new QgsStaticExpressionFunction( QStringLiteral( "scale_exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << 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" ) )
6582  << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
6583  << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
6584  << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
6585  << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
6586  << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
6587  << 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" ) )
6588  << 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" ) )
6589  << 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" ) )
6590  << 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" ) )
6591  << 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" ) )
6592  << 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" ) )
6593  << 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" ) )
6594  << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
6595  << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
6596  << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
6597  << 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 )
6598  << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
6599 
6600  << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
6602  << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
6603  << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
6604  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
6605  << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
6606  << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
6607  << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
6608  fcnAggregate,
6609  QStringLiteral( "Aggregates" ),
6610  QString(),
6611  []( const QgsExpressionNodeFunction * node )
6612  {
6613  // usesGeometry callback: return true if @parent variable is referenced
6614 
6615  if ( !node )
6616  return true;
6617 
6618  if ( !node->args() )
6619  return false;
6620 
6621  QSet<QString> referencedVars;
6622  if ( node->args()->count() > 2 )
6623  {
6624  QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
6625  referencedVars = subExpressionNode->referencedVariables();
6626  }
6627 
6628  if ( node->args()->count() > 3 )
6629  {
6630  QgsExpressionNode *filterNode = node->args()->at( 3 );
6631  referencedVars.unite( filterNode->referencedVariables() );
6632  }
6633  return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
6634  },
6635  []( const QgsExpressionNodeFunction * node )
6636  {
6637  // referencedColumns callback: return AllAttributes if @parent variable is referenced
6638 
6639  if ( !node )
6640  return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
6641 
6642  if ( !node->args() )
6643  return QSet<QString>();
6644 
6645  QSet<QString> referencedCols;
6646  QSet<QString> referencedVars;
6647 
6648  if ( node->args()->count() > 2 )
6649  {
6650  QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
6651  referencedVars = subExpressionNode->referencedVariables();
6652  referencedCols = subExpressionNode->referencedColumns();
6653  }
6654  if ( node->args()->count() > 3 )
6655  {
6656  QgsExpressionNode *filterNode = node->args()->at( 3 );
6657  referencedVars = filterNode->referencedVariables();
6658  referencedCols.unite( filterNode->referencedColumns() );
6659  }
6660 
6661  if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
6662  return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
6663  else
6664  return referencedCols;
6665  },
6666  true
6667  )
6668 
6669  << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
6670  << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
6671  << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
6672  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
6673  << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
6674  << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
6675  fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
6676 
6677  << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6678  << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6679  << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6680  << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6681  << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6682  << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6683  << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6684  << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6685  << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6686  << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6687  << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6688  << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6689  << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6690  << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6691  << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6692  << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6693  << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6694  << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6695  << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6696  << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6697  << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
6698 
6699  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
6700  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
6701 
6702  << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
6703  << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
6704  << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
6705  fcnAge, QStringLiteral( "Date and Time" ) )
6706  << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
6707  << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
6708  << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
6709  << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
6710  << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
6711  << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
6712  << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
6713  << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
6714  << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
6715  << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
6716  << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
6717  << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
6718  << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
6719  fcnMakeDate, QStringLiteral( "Date and Time" ) )
6720  << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
6721  << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
6722  << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
6723  fcnMakeTime, QStringLiteral( "Date and Time" ) )
6724  << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
6725  << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
6726  << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
6727  << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
6728  << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
6729  << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
6730  fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
6731  << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
6732  << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
6733  << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
6734  << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
6735  << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
6736  << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
6737  << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
6738  fcnMakeInterval, QStringLiteral( "Date and Time" ) )
6739  << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
6740  << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
6741  << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
6742  << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
6743  << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
6744  << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
6745  << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
6746  << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
6747  << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
6748  << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
6749  << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
6750  << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
6751  << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
6752  << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
6753  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
6754  << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
6755  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
6756  << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
6757  false, QSet< QString >(), false, QStringList(), true )
6758  << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
6759  << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
6760  << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
6761  << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
6762  << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
6763  << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
6764  << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
6765  << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnFormatNumber, QStringLiteral( "String" ) )
6766  << 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" ) )
6767  << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
6768  << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
6769  << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
6770  << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
6771  fcnColorMixRgb, QStringLiteral( "Color" ) )
6772  << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
6773  << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
6774  << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
6775  fcnColorRgb, QStringLiteral( "Color" ) )
6776  << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
6777  << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
6778  << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
6779  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
6780  fncColorRgba, QStringLiteral( "Color" ) )
6781  << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
6782  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
6783  fcnRampColor, QStringLiteral( "Color" ) )
6784  << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
6785  << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
6786  fcnCreateRamp, QStringLiteral( "Color" ) )
6787  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
6788  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
6789  << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
6790  fcnColorHsl, QStringLiteral( "Color" ) )
6791  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
6792  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
6793  << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
6794  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
6795  fncColorHsla, QStringLiteral( "Color" ) )
6796  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
6797  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
6798  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
6799  fcnColorHsv, QStringLiteral( "Color" ) )
6800  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
6801  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
6802  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
6803  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
6804  fncColorHsva, QStringLiteral( "Color" ) )
6805  << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
6806  << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
6807  << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
6808  << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
6809  fcnColorCmyk, QStringLiteral( "Color" ) )
6810  << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
6811  << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
6812  << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
6813  << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
6814  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
6815  fncColorCmyka, QStringLiteral( "Color" ) )
6816  << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
6817  << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
6818  fncColorPart, QStringLiteral( "Color" ) )
6819  << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
6820  << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
6821  fncDarker, QStringLiteral( "Color" ) )
6822  << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
6823  << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
6824  fncLighter, QStringLiteral( "Color" ) )
6825  << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
6826 
6827  // file info
6828  << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
6829  fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
6830  << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
6831  fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
6832  << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
6833  fcnFileExists, QStringLiteral( "Files and Paths" ) )
6834  << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
6835  fcnFileName, QStringLiteral( "Files and Paths" ) )
6836  << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
6837  fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
6838  << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
6839  fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
6840  << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
6841  fcnFilePath, QStringLiteral( "Files and Paths" ) )
6842  << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
6843  fcnFileSize, QStringLiteral( "Files and Paths" ) )
6844 
6845  << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
6846  fcnExif, QStringLiteral( "Files and Paths" ) )
6847  << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
6848  fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
6849 
6850  // hash
6851  << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
6852  fcnGenericHash, QStringLiteral( "Conversions" ) )
6853  << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
6854  fcnHashMd5, QStringLiteral( "Conversions" ) )
6855  << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
6856  fcnHashSha256, QStringLiteral( "Conversions" ) )
6857 
6858  //base64
6859  << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
6860  fcnToBase64, QStringLiteral( "Conversions" ) )
6861  << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
6862  fcnFromBase64, QStringLiteral( "Conversions" ) )
6863 
6864  // deprecated stuff - hidden from users
6865  << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
6866 
6867  QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
6868  geomFunc->setIsStatic( false );
6869  functions << geomFunc;
6870 
6871  QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
6872  areaFunc->setIsStatic( false );
6873  functions << areaFunc;
6874 
6875  functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
6876 
6877  QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
6878  lengthFunc->setIsStatic( false );
6879  functions << lengthFunc;
6880 
6881  QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
6882  perimeterFunc->setIsStatic( false );
6883  functions << perimeterFunc;
6884 
6885  functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
6886 
6887  QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
6888  xFunc->setIsStatic( false );
6889  functions << xFunc;
6890 
6891  QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
6892  yFunc->setIsStatic( false );
6893  functions << yFunc;
6894 
6895  QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
6896  zFunc->setIsStatic( false );
6897  functions << zFunc;
6898 
6899  QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
6900  {
6901  { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
6902  { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
6903  { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
6904  { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
6905  { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
6906  { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
6907  { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
6908  };
6909  QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
6910  while ( i.hasNext() )
6911  {
6912  i.next();
6914  << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
6915  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
6916  << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
6917  << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
6918  << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
6919  i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
6920 
6921  // The current feature is accessed for the geometry, so this should not be cached
6922  fcnGeomOverlayFunc->setIsStatic( false );
6923  functions << fcnGeomOverlayFunc;
6924  }
6925 
6926  QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
6927  << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
6928  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
6929  << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
6930  << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
6931  << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
6932  << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
6933  fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
6934  // The current feature is accessed for the geometry, so this should not be cached
6935  fcnGeomOverlayNearestFunc->setIsStatic( false );
6936  functions << fcnGeomOverlayNearestFunc;
6937 
6938  functions
6939  << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
6940  << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
6941  << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
6942  << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
6943  << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
6944  << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
6945  << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
6946  << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
6947  << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
6948  << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
6949  fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
6950  << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
6951  << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
6952  << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
6953  << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
6954  << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
6955  << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
6956  fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
6957  << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
6958  << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
6959  << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
6960  << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
6961  << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
6962  fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
6963  << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
6964  << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
6965  << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
6966  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
6967  fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
6968  << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
6969  << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
6970  << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
6971  << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
6972  << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
6973  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
6974  fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
6975  << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
6976  << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
6977  << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
6978  << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
6979  << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
6980  fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
6981  << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
6982  << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
6983  << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
6984  fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
6985  << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
6986  << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
6987  << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
6988  << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
6989  << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
6990  fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) );
6991  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" ) );
6992  xAtFunc->setIsStatic( false );
6993  functions << xAtFunc;
6994 
6995  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" ) );
6996  yAtFunc->setIsStatic( false );
6997  functions << yAtFunc;
6998 
6999  functions
7000  << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
7001  << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
7002  << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
7003  << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
7004  << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
7005  << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
7006  << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
7007  << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
7008  << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
7009  << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
7010  << new QgsStaticExpressionFunction( QStringLiteral( "intersects_bbox" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ), fcnBbox, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "bbox" ) )
7011  << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7012  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7013  fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
7014  << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7015  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7016  fcnIntersects, QStringLiteral( "GeometryGroup" ) )
7017  << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7018  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7019  fcnTouches, QStringLiteral( "GeometryGroup" ) )
7020  << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7021  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7022  fcnCrosses, QStringLiteral( "GeometryGroup" ) )
7023  << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7024  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7025  fcnContains, QStringLiteral( "GeometryGroup" ) )
7026  << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7027  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7028  fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
7029  << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7030  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7031  fcnWithin, QStringLiteral( "GeometryGroup" ) )
7032  << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7033  << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
7034  << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
7035  fcnTranslate, QStringLiteral( "GeometryGroup" ) )
7036  << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7037  << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
7038  << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
7039  fcnRotate, QStringLiteral( "GeometryGroup" ) )
7040  << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7041  << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
7042  << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
7043  << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
7044  << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
7045  << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
7046  << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
7047  << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
7048  << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
7049  << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
7050  fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
7051  << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7052  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
7053  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
7054  << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
7055  << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
7056  << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
7057  fcnBuffer, QStringLiteral( "GeometryGroup" ) )
7058  << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7059  fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
7060  << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7061  << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
7062  << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
7063  << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
7064  << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
7065  << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7066  << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
7067  << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
7068  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
7069  , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
7070  << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7071  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
7072  , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
7073  << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7074  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
7075  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
7076  << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
7077  << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
7078  fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
7079  << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7080  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
7081  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
7082  << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
7083  << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
7084  fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
7085  << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7086  << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
7087  << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
7088  fcnExtend, QStringLiteral( "GeometryGroup" ) )
7089  << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
7090  << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
7091  << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7092  << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
7093  << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, QStringLiteral( "GeometryGroup" ) )
7094  << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
7095  << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7096  << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
7097  fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
7098  << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7099  << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
7100  fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
7101  << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
7102  << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
7103  << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
7104  << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
7105  << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
7106  << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
7107  << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
7108  << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
7109  << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
7110  << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
7111  << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
7112  << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
7113  << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
7114  << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
7115  << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
7116  << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
7117  << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
7118  << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
7119  << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
7120  << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
7121  << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
7122  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7123  fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
7124  << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
7125  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7126  fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
7127  << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
7128  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7129  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
7130  fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
7131  << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7132  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7133  fcnDifference, QStringLiteral( "GeometryGroup" ) )
7134  << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7135  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7136  fcnDistance, QStringLiteral( "GeometryGroup" ) )
7137  << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
7138  << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
7139  fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
7140  << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7141  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7142  fcnIntersection, QStringLiteral( "GeometryGroup" ) )
7143  << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7144  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7145  fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
7146  << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7147  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7148  fcnCombine, QStringLiteral( "GeometryGroup" ) )
7149  << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7150  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7151  fcnCombine, QStringLiteral( "GeometryGroup" ) )
7152  << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7153  << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
7154  fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
7155  << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7156  fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
7157  << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
7158  << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7159  << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
7160  << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
7161  fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
7162  << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7163  << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
7164  << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
7165  fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
7166  << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7167  fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
7168  << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7169  fcnZMax, QStringLiteral( "GeometryGroup" ) )
7170  << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7171  fcnZMin, QStringLiteral( "GeometryGroup" ) )
7172  << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7173  fcnMMax, QStringLiteral( "GeometryGroup" ) )
7174  << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7175  fcnMMin, QStringLiteral( "GeometryGroup" ) )
7176  << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7177  fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
7178  << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7179  fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
7180 
7181 
7182  QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7183  << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
7184  << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
7185  fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
7186 
7187  orderPartsFunc->setIsStaticFunction(
7188  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
7189  {
7190  const QList< QgsExpressionNode *> argList = node->args()->list();
7191  for ( QgsExpressionNode *argNode : argList )
7192  {
7193  if ( !argNode->isStatic( parent, context ) )
7194  return false;
7195  }
7196 
7197  if ( node->args()->count() > 1 )
7198  {
7199  QgsExpressionNode *argNode = node->args()->at( 1 );
7200 
7201  QString expString = argNode->eval( parent, context ).toString();
7202 
7203  QgsExpression e( expString );
7204 
7205  if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
7206  return true;
7207  }
7208 
7209  return true;
7210  } );
7211 
7212  orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
7213  {
7214  if ( node->args()->count() > 1 )
7215  {
7216  QgsExpressionNode *argNode = node->args()->at( 1 );
7217  QString expression = argNode->eval( parent, context ).toString();
7218  QgsExpression e( expression );
7219  e.prepare( context );
7220  context->setCachedValue( expression, QVariant::fromValue( e ) );
7221  }
7222  return true;
7223  }
7224  );
7225  functions << orderPartsFunc;
7226 
7227  functions
7228  << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7229  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7230  fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
7231  << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7232  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7233  fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
7234  << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7235  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
7236  << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7237  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
7238  << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7239  << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
7240  << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7241  << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
7242  << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7243  << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
7244  << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7245  << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
7246 
7247 
7248  // **Record** functions
7249 
7250  QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
7251  idFunc->setIsStatic( false );
7252  functions << idFunc;
7253 
7254  QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
7255  currentFeatureFunc->setIsStatic( false );
7256  functions << currentFeatureFunc;
7257 
7258  QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction( QStringLiteral( "uuid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QStringLiteral( "WithBraces" ) ), fcnUuid, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$uuid" ) );
7259  uuidFunc->setIsStatic( false );
7260  functions << uuidFunc;
7261 
7262  functions
7263  << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7264  << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
7265  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
7266  fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
7267  << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7268  << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
7269  fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
7270 
7271  QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
7272  fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
7273  attributesFunc->setIsStatic( false );
7274  functions << attributesFunc;
7275 
7277  QStringLiteral( "maptip" ),
7278  -1,
7279  fcnFeatureMaptip,
7280  QStringLiteral( "Record and Attributes" ),
7281  QString(),
7282  false,
7283  QSet<QString>()
7284  );
7285  maptipFunc->setIsStatic( false );
7286  functions << maptipFunc;
7287 
7289  QStringLiteral( "display_expression" ),
7290  -1,
7291  fcnFeatureDisplayExpression,
7292  QStringLiteral( "Record and Attributes" ),
7293  QString(),
7294  false,
7295  QSet<QString>()
7296  );
7297  displayFunc->setIsStatic( false );
7298  functions << displayFunc;
7299 
7301  QStringLiteral( "is_selected" ),
7302  -1,
7303  fcnIsSelected,
7304  QStringLiteral( "Record and Attributes" ),
7305  QString(),
7306  false,
7307  QSet<QString>()
7308  );
7309  isSelectedFunc->setIsStatic( false );
7310  functions << isSelectedFunc;
7311 
7312  functions
7314  QStringLiteral( "num_selected" ),
7315  -1,
7316  fcnNumSelected,
7317  QStringLiteral( "Record and Attributes" ),
7318  QString(),
7319  false,
7320  QSet<QString>()
7321  );
7322 
7323  functions
7325  QStringLiteral( "sqlite_fetch_and_increment" ),
7327  << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
7328  << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
7329  << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
7330  << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
7331  << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
7332  << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
7333  fcnSqliteFetchAndIncrement,
7334  QStringLiteral( "Record and Attributes" )
7335  );
7336 
7337  // **Fields and Values** functions
7338  QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
7339 
7340  representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
7341  {
7342  Q_UNUSED( context )
7343  if ( node->args()->count() == 1 )
7344  {
7345  QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
7346  if ( colRef )
7347  {
7348  return true;
7349  }
7350  else
7351  {
7352  parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
7353  return false;
7354  }
7355  }
7356  else if ( node->args()->count() == 2 )
7357  {
7358  return true;
7359  }
7360  else
7361  {
7362  parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
7363  return false;
7364  }
7365  }
7366  );
7367 
7368  functions << representValueFunc;
7369 
7370  // **General** functions
7371  functions
7372  << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7373  << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
7374  fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
7375  << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
7377  << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7378  << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
7379  fcnDecodeUri, QStringLiteral( "Map Layers" ) )
7380  << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
7382  << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
7383  fcnMimeType, QStringLiteral( "General" ) )
7384  << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7385  << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
7386  << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
7387 
7388  // **var** function
7389  QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
7390  varFunction->setIsStaticFunction(
7391  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
7392  {
7393  /* A variable node is static if it has a static name and the name can be found at prepare
7394  * time and is tagged with isStatic.
7395  * It is not static if a variable is set during iteration or not tagged isStatic.
7396  * (e.g. geom_part variable)
7397  */
7398  if ( node->args()->count() > 0 )
7399  {
7400  QgsExpressionNode *argNode = node->args()->at( 0 );
7401 
7402  if ( !argNode->isStatic( parent, context ) )
7403  return false;
7404 
7405  QString varName = argNode->eval( parent, context ).toString();
7406 
7407  const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
7408  return scope ? scope->isStatic( varName ) : false;
7409  }
7410  return false;
7411  }
7412  );
7413 
7414  functions
7415  << varFunction;
7416 
7417  functions << new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true );
7418 
7419  QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
7420  evalFunc->setIsStaticFunction(
7421  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
7422  {
7423  if ( node->args()->count() > 0 )
7424  {
7425  QgsExpressionNode *argNode = node->args()->at( 0 );
7426 
7427  if ( argNode->isStatic( parent, context ) )
7428  {
7429  QString expString = argNode->eval( parent, context ).toString();
7430 
7431  QgsExpression e( expString );
7432 
7433  if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
7434  return true;
7435  }
7436  }
7437 
7438  return false;
7439  } );
7440 
7441  functions << evalFunc;
7442 
7443  QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
7444  attributeFunc->setIsStaticFunction(
7445  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
7446  {
7447  const QList< QgsExpressionNode *> argList = node->args()->list();
7448  for ( QgsExpressionNode *argNode : argList )
7449  {
7450  if ( !argNode->isStatic( parent, context ) )
7451  return false;
7452  }
7453 
7454  if ( node->args()->count() == 1 )
7455  {
7456  // not static -- this is the variant which uses the current feature taken direct from the expression context
7457  return false;
7458  }
7459 
7460  return true;
7461  } );
7462  functions << attributeFunc;
7463 
7464  functions
7465  << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
7467  << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
7468 
7469  // functions for arrays
7472  << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
7473  << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
7474  << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
7475  << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
7476  << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
7477  << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
7478  << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
7479  << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
7480  << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
7481  << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
7482  << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
7483  << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
7484  << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
7485  << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
7486  << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
7487  << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
7488  << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
7489  << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
7490  << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
7491  << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
7492  << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
7493  << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ) )
7494  << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
7495  << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
7496  << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
7497  << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
7498  << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
7499  << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
7500  << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
7501  << 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" ) )
7502  << 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" ) )
7503  << 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" ) )
7504 
7505  //functions for maps
7506  << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
7507  << new QgsStaticExpressionFunction( QStringLiteral( "to_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "json_string" ) ), fcnWriteJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "map_to_json" ) )
7508  << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
7509  << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
7510  << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
7511  << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
7512  << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
7513  << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
7514  << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
7515  << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
7516  << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
7517  << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
7518  ;
7519 
7521 
7522  //QgsExpression has ownership of all built-in functions
7523  for ( QgsExpressionFunction *func : std::as_const( functions ) )
7524  {
7525  *sOwnedFunctions() << func;
7526  *sBuiltinFunctions() << func->name();
7527  sBuiltinFunctions()->append( func->aliases() );
7528  }
7529  }
7530  return functions;
7531 }
7532 
7533 bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
7534 {
7535  int fnIdx = functionIndex( function->name() );
7536  if ( fnIdx != -1 )
7537  {
7538  return false;
7539  }
7540  sFunctions()->append( function );
7541  if ( transferOwnership )
7542  sOwnedFunctions()->append( function );
7543  return true;
7544 }
7545 
7546 bool QgsExpression::unregisterFunction( const QString &name )
7547 {
7548  // You can never override the built in functions.
7549  if ( QgsExpression::BuiltinFunctions().contains( name ) )
7550  {
7551  return false;
7552  }
7553  int fnIdx = functionIndex( name );
7554  if ( fnIdx != -1 )
7555  {
7556  sFunctions()->removeAt( fnIdx );
7557  return true;
7558  }
7559  return false;
7560 }
7561 
7563 {
7564  qDeleteAll( *sOwnedFunctions() );
7565  sOwnedFunctions()->clear();
7566 }
7567 
7569 {
7570  if ( sBuiltinFunctions()->isEmpty() )
7571  {
7572  Functions(); // this method builds the gmBuiltinFunctions as well
7573  }
7574  return *sBuiltinFunctions();
7575 }
7576 
7577 
7579  : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
7580  << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
7581  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
7582  QStringLiteral( "Arrays" ) )
7583 {
7584 
7585 }
7586 
7588 {
7589  bool isStatic = false;
7590 
7591  QgsExpressionNode::NodeList *args = node->args();
7592 
7593  if ( args->count() < 2 )
7594  return false;
7595 
7596  if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
7597  {
7598  isStatic = true;
7599  }
7600  return isStatic;
7601 }
7602 
7604 {
7605  Q_UNUSED( node )
7606  QVariantList result;
7607 
7608  if ( args->count() < 2 )
7609  // error
7610  return result;
7611 
7612  QVariantList array = args->at( 0 )->eval( parent, context ).toList();
7613 
7614  QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
7615  std::unique_ptr< QgsExpressionContext > tempContext;
7616  if ( !subContext )
7617  {
7618  tempContext = std::make_unique< QgsExpressionContext >();
7619  subContext = tempContext.get();
7620  }
7621 
7623  subContext->appendScope( subScope );
7624 
7625  for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7626  {
7627  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
7628  result << args->at( 1 )->eval( parent, subContext );
7629  }
7630 
7631  if ( context )
7632  delete subContext->popScope();
7633 
7634  return result;
7635 }
7636 
7637 QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7638 {
7639  // This is a dummy function, all the real handling is in run
7640  Q_UNUSED( values )
7641  Q_UNUSED( context )
7642  Q_UNUSED( parent )
7643  Q_UNUSED( node )
7644 
7645  Q_ASSERT( false );
7646  return QVariant();
7647 }
7648 
7650 {
7651  QgsExpressionNode::NodeList *args = node->args();
7652 
7653  if ( args->count() < 2 )
7654  // error
7655  return false;
7656 
7657  args->at( 0 )->prepare( parent, context );
7658 
7659  QgsExpressionContext subContext;
7660  if ( context )
7661  subContext = *context;
7662 
7664  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
7665  subContext.appendScope( subScope );
7666 
7667  args->at( 1 )->prepare( parent, &subContext );
7668 
7669  return true;
7670 }
7671 
7673  : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
7674  << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
7675  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
7676  << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
7677  QStringLiteral( "Arrays" ) )
7678 {
7679 
7680 }
7681 
7683 {
7684  bool isStatic = false;
7685 
7686  QgsExpressionNode::NodeList *args = node->args();
7687 
7688  if ( args->count() < 2 )
7689  return false;
7690 
7691  if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
7692  {
7693  isStatic = true;
7694  }
7695  return isStatic;
7696 }
7697 
7699 {
7700  Q_UNUSED( node )
7701  QVariantList result;
7702 
7703  if ( args->count() < 2 )
7704  // error
7705  return result;
7706 
7707  const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
7708 
7709  QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
7710  std::unique_ptr< QgsExpressionContext > tempContext;
7711  if ( !subContext )
7712  {
7713  tempContext = std::make_unique< QgsExpressionContext >();
7714  subContext = tempContext.get();
7715  }
7716 
7718  subContext->appendScope( subScope );
7719 
7720  int limit = 0;
7721  if ( args->count() >= 3 )
7722  {
7723  const QVariant limitVar = args->at( 2 )->eval( parent, context );
7724 
7725  if ( QgsExpressionUtils::isIntSafe( limitVar ) )
7726  {
7727  limit = limitVar.toInt();
7728  }
7729  else
7730  {
7731  return result;
7732  }
7733  }
7734 
7735  for ( const QVariant &value : array )
7736  {
7737  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
7738  if ( args->at( 1 )->eval( parent, subContext ).toBool() )
7739  {
7740  result << value;
7741 
7742  if ( limit > 0 && limit == result.size() )
7743  break;
7744  }
7745  }
7746 
7747  if ( context )
7748  delete subContext->popScope();
7749 
7750  return result;
7751 }
7752 
7753 QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7754 {
7755  // This is a dummy function, all the real handling is in run
7756  Q_UNUSED( values )
7757  Q_UNUSED( context )
7758  Q_UNUSED( parent )
7759  Q_UNUSED( node )
7760 
7761  Q_ASSERT( false );
7762  return QVariant();
7763 }
7764 
7766 {
7767  QgsExpressionNode::NodeList *args = node->args();
7768 
7769  if ( args->count() < 2 )
7770  // error
7771  return false;
7772 
7773  args->at( 0 )->prepare( parent, context );
7774 
7775  QgsExpressionContext subContext;
7776  if ( context )
7777  subContext = *context;
7778 
7780  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
7781  subContext.appendScope( subScope );
7782 
7783  args->at( 1 )->prepare( parent, &subContext );
7784 
7785  return true;
7786 }
7788  : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
7789  QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
7790  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
7791  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
7792  QStringLiteral( "General" ) )
7793 {
7794 
7795 }
7796 
7798 {
7799  bool isStatic = false;
7800 
7801  QgsExpressionNode::NodeList *args = node->args();
7802 
7803  if ( args->count() < 3 )
7804  return false;
7805 
7806  // We only need to check if the node evaluation is static, if both - name and value - are static.
7807  if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
7808  {
7809  QVariant name = args->at( 0 )->eval( parent, context );
7810  QVariant value = args->at( 1 )->eval( parent, context );
7811 
7812  // Temporarily append a new scope to provide the variable
7813  appendTemporaryVariable( context, name.toString(), value );
7814  if ( args->at( 2 )->isStatic( parent, context ) )
7815  isStatic = true;
7816  popTemporaryVariable( context );
7817  }
7818 
7819  return isStatic;
7820 }
7821 
7823 {
7824  Q_UNUSED( node )
7825  QVariant result;
7826 
7827  if ( args->count() < 3 )
7828  // error
7829  return result;
7830 
7831  QVariant name = args->at( 0 )->eval( parent, context );
7832  QVariant value = args->at( 1 )->eval( parent, context );
7833 
7834  const QgsExpressionContext *updatedContext = context;
7835  std::unique_ptr< QgsExpressionContext > tempContext;
7836  if ( !updatedContext )
7837  {
7838  tempContext = std::make_unique< QgsExpressionContext >();
7839  updatedContext = tempContext.get();
7840  }
7841 
7842  appendTemporaryVariable( updatedContext, name.toString(), value );
7843  result = args->at( 2 )->eval( parent, updatedContext );
7844 
7845  if ( context )
7846  popTemporaryVariable( updatedContext );
7847 
7848  return result;
7849 }
7850 
7851 QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7852 {
7853  // This is a dummy function, all the real handling is in run
7854  Q_UNUSED( values )
7855  Q_UNUSED( context )
7856  Q_UNUSED( parent )
7857  Q_UNUSED( node )
7858 
7859  Q_ASSERT( false );
7860  return QVariant();
7861 }
7862 
7864 {
7865  QgsExpressionNode::NodeList *args = node->args();
7866 
7867  if ( args->count() < 3 )
7868  // error
7869  return false;
7870 
7871  QVariant name = args->at( 0 )->prepare( parent, context );
7872  QVariant value = args->at( 1 )->prepare( parent, context );
7873 
7874  const QgsExpressionContext *updatedContext = context;
7875  std::unique_ptr< QgsExpressionContext > tempContext;
7876  if ( !updatedContext )
7877  {
7878  tempContext = std::make_unique< QgsExpressionContext >();
7879  updatedContext = tempContext.get();
7880  }
7881 
7882  appendTemporaryVariable( updatedContext, name.toString(), value );
7883  args->at( 2 )->prepare( parent, updatedContext );
7884 
7885  if ( context )
7886  popTemporaryVariable( updatedContext );
7887 
7888  return true;
7889 }
7890 
7891 void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
7892 {
7893  QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
7894  delete updatedContext->popScope();
7895 }
7896 
7897 void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
7898 {
7901 
7902  QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
7903  updatedContext->appendScope( scope );
7904 }
@ Success
Operation succeeded.
JoinStyle
Join styles for buffers.
Definition: qgis.h:724
EndCapStyle
End cap styles for buffers.
Definition: qgis.h:711
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const SIP_HOLDGIL
Returns a reference to the simplest lossless representation of this geometry, e.g.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
QString abstract() const
Returns a free-form description of the resource.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource.
Aggregate
Available aggregates to calculate.
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
@ StringMaximumLength
Maximum length of string (string fields only)
@ ThirdQuartile
Third quartile (numeric fields only)
@ Range
Range of values (max - min) (numeric and datetime fields only)
@ ArrayAggregate
Create an array of values.
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only)
@ FirstQuartile
First quartile (numeric fields only)
@ Median
Median of values (numeric fields only)
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
@ CountMissing
Number of missing (null) values.
@ StDevSample
Sample standard deviation of values (numeric fields only)
@ Majority
Majority of values.
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
@ Mean
Mean of values (numeric fields only)
@ StringMinimumLength
Minimum length of string (string fields only)
@ CountDistinct
Number of distinct values.
@ Minority
Minority of values.
static Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Handles the array_filter(array, expression) expression function.
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.
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 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's...
Handles the array loopingarray_Foreach(array, expression) expression function.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
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 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's...
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.
Circle geometry type.
Definition: qgscircle.h:44
Abstract base class for color ramps.
Definition: qgscolorramp.h:32
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
QString description() const
Returns the descriptive name of the CRS, e.g., "WGS 84" or "GDA 94 / Vicgrid94".
QString authid() const
Returns the authority identifier for the CRS.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Curve polygon geometry type.
int ringCount(int part=0) const override SIP_HOLDGIL
Returns the number of rings of which this geometry is built.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition: qgscurve.cpp:277
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:175
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:53
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition: qgscurve.cpp:272
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double convertAreaMeasurement(double area, QgsUnitTypes::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
double convertLengthMeasurement(double length, QgsUnitTypes::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
Holder for the widget type and its configuration for a field.
QVariantMap config() const
Ellipse geometry type.
Definition: qgsellipse.h:40
QString what() const
Definition: qgsexception.h:48
Contains utilities for working with EXIF tags in images.
Definition: qgsexiftools.h:33
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
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.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
Represents a single parameter passed to a function.
A abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
bool lazyEval() const
true if this function should use lazy evaluation.
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...
QString name() const
The name of the function.
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's...
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
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.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes it value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for literal values.
A list of expression nodes.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
int count() const
Returns the number of nodes in the list.
Abstract base class for all nodes that can appear in an expression.
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
A set of expression-related functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
QgsUnitTypes::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
static QString helpText(QString name)
Returns the help text for a specified function.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
QgsUnitTypes::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsFields fields
Definition: qgsfeature.h:66
QgsGeometry geometry
Definition: qgsfeature.h:67
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:209
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:320
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:163
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
QString name
Definition: qgsfield.h:60
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:572
Container of fields for a vector layer.
Definition: qgsfields.h:45
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
Geometry collection.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry difference(const QgsGeometry &geometry) const
Returns a geometry representing the points making up this geometry that do not make up other.
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
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.
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QgsGeometry combine(const QgsGeometry &geometry) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
Q_GADGET bool isNull
Definition: qgsgeometry.h:127
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
bool isMultipart() const SIP_HOLDGIL
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:128
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
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...
double area() const
Returns the planar, 2-dimensional area of the geometry.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QString lastError() const SIP_HOLDGIL
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
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...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsGeometry symDifference(const QgsGeometry &geometry) const
Returns a geometry representing the points making up this geometry that do not make up other.
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
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...
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
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.
QString asWkt(int precision=17) const
Exports the geometry to WKT.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:153
Represents a color stop within a QgsGradientColorRamp color ramp.
Definition: qgscolorramp.h:113
A representation of the interval between two datetime values.
Definition: qgsinterval.h:42
bool isValid() const
Returns true if the interval is valid.
Definition: qgsinterval.h:255
double days() const
Returns the interval duration in days.
double weeks() const
Returns the interval duration in weeks.
double months() const
Returns the interval duration in months (based on a 30 day month).
double seconds() const
Returns the interval duration in seconds.
Definition: qgsinterval.h:236
double years() const
Returns the interval duration in years (based on an average year length)
double hours() const
Returns the interval duration in hours.
double minutes() const
Returns the interval duration in minutes.
QStringList rights() const
Returns a list of attribution or copyright strings associated with the resource.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
virtual QgsRectangle extent() const
Returns the extent of the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsMapLayerType type
Definition: qgsmaplayer.h:80
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QgsLayerMetadata metadata
Definition: qgsmaplayer.h:78
QString abstract() const
Returns the abstract of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:323
virtual bool isEditable() const
Returns true if the layer can be edited.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:307
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:359
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:415
double minimumScale() const
Returns the minimum map scale (i.e.
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double maximumScale() const
Returns the maximum map scale (i.e.
QString keywordList() const
Returns the keyword list of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:339
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
Definition: qgsmultipoint.h:30
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override SIP_HOLDGIL
Checks validity of the geometry, and returns true if the geometry is valid.
Definition: qgspoint.cpp:424
QgsPoint project(double distance, double azimuth, double inclination=90.0) const SIP_HOLDGIL
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition: qgspoint.cpp:735
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
double inclination(const QgsPoint &other) const SIP_HOLDGIL
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition: qgspoint.cpp:723
QgsRelationManager * relationManager
Definition: qgsproject.h:111
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Quadrilateral geometry type.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2) SIP_HOLDGIL
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode) SIP_HOLDGIL
Construct a QgsQuadrilateral as a Rectangle from 3 points.
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double sum
The sum of all cells in the band. NO_DATA values are excluded.
double maximumValue
The maximum cell value in the raster band.
double range
The range is the distance between min & max.
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.
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
Represents a raster layer.
int bandCount() const
Returns the number of bands in this layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
void grow(double delta)
Grows the rectangle in place by the specified amount.
Definition: qgsrectangle.h:296
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
Regular Polygon geometry type.
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
@ CircumscribedCircle
Circumscribed about a circle (the radius is the distance from the center to the midpoints of the side...
@ InscribedCircle
Inscribed in a circle (the radius is the distance between the center and vertices)
QgsPolygon * toPolygon() const
Returns as a polygon.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:48
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:47
bool isValid
Definition: qgsrelation.h:50
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
c++ helper class for defining QgsExpression functions.
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.
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.
QStringList aliases() const override
Returns a list of possible aliases for the function.
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.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
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...
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
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.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance)
Definition: qgsstyle.cpp:452
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:131
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
This class allows including a set of layers in a database-side transaction, provided the layer data p...
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
Triangle geometry type.
Definition: qgstriangle.h:34
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QString mapTipTemplate
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QVariant aggregate(QgsAggregateCalculator::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
Handles the with_variable(name, value, node) expression function.
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 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's...
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.
static QString geometryDisplayString(GeometryType type) SIP_HOLDGIL
Returns a display string for a geometry type.
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
@ PointCloudLayer
Added in 3.18.
@ MeshLayer
Added in 3.2.
@ VectorTileLayer
Added in 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
CORE_EXPORT QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
CORE_EXPORT QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
CORE_EXPORT QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
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
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
uint qHash(const QVariant &variant)
Hash for QVariant.
Definition: qgis.cpp:225
#define str(x)
Definition: qgis.cpp:37
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:127
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition: qgis.h:1300
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1246
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Definition: qgscolorramp.h:138
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
bool(QgsGeometry::* RelationFunction)(const QgsGeometry &geometry) const
QList< QgsExpressionFunction * > ExpressionFunctionList
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction *)
#define ENSURE_NO_EVAL_ERROR
#define FEAT_FROM_CONTEXT(c, f)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition: qgsgeometry.h:82
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2130
Q_DECLARE_METATYPE(QgsMeshTimeSettings)
QLineF segment(int index, QRectF rect, double radius)
int precision
A bundle of parameters controlling aggregate calculation.
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
Single variable definition for use within a QgsExpressionContextScope.
The Context struct stores the current layer and coordinate transform context.
Definition: qgsogcutils.h:60
const QgsMapLayer * layer
Definition: qgsogcutils.h:70
QgsCoordinateTransformContext transformContext
Definition: qgsogcutils.h:71
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31