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