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