QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
65 
66 Q_GLOBAL_STATIC( ExpressionFunctionList, sOwnedFunctions )
67 Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
69 
72 Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
73 
74 const QString QgsExpressionFunction::helpText() const
75 {
76  return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
77 }
78 
80 {
81  Q_UNUSED( node )
82  // evaluate arguments
83  QVariantList argValues;
84  if ( args )
85  {
86  int arg = 0;
87  const QList< QgsExpressionNode * > argList = args->list();
88  for ( QgsExpressionNode *n : argList )
89  {
90  QVariant v;
91  if ( lazyEval() )
92  {
93  // Pass in the node for the function to eval as it needs.
94  v = QVariant::fromValue( n );
95  }
96  else
97  {
98  v = n->eval( parent, context );
100  bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
101  if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
102  return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
103  }
104  argValues.append( v );
105  arg++;
106  }
107  }
108 
109  return func( argValues, context, parent, node );
110 }
111 
113 {
114  Q_UNUSED( node )
115  return true;
116 }
117 
119 {
120  return QStringList();
121 }
122 
124 {
125  Q_UNUSED( parent )
126  Q_UNUSED( context )
127  Q_UNUSED( node )
128  return false;
129 }
130 
132 {
133  Q_UNUSED( parent )
134  Q_UNUSED( context )
135  Q_UNUSED( node )
136  return true;
137 }
138 
140 {
141  Q_UNUSED( node )
142  return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
143 }
144 
146 {
147  return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
148 }
149 
151 {
152  return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
153 }
154 
156 {
157  return mHandlesNull;
158 }
159 
160 // doxygen doesn't like this constructor for some reason (maybe the function arguments?)
163  FcnEval fcn,
164  const QString &group,
165  const QString &helpText,
166  const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
167  const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
168  bool lazyEval,
169  const QStringList &aliases,
170  bool handlesNull )
171  : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
172  , mFnc( fcn )
173  , mAliases( aliases )
174  , mUsesGeometry( false )
175  , mUsesGeometryFunc( usesGeometry )
176  , mReferencedColumnsFunc( referencedColumns )
177 {
178 }
180 
182 {
183  return mAliases;
184 }
185 
187 {
188  if ( mUsesGeometryFunc )
189  return mUsesGeometryFunc( node );
190  else
191  return mUsesGeometry;
192 }
193 
195 {
196  if ( mReferencedColumnsFunc )
197  return mReferencedColumnsFunc( node );
198  else
199  return mReferencedColumns;
200 }
201 
203 {
204  if ( mIsStaticFunc )
205  return mIsStaticFunc( node, parent, context );
206  else
207  return mIsStatic;
208 }
209 
211 {
212  if ( mPrepareFunc )
213  return mPrepareFunc( node, parent, context );
214 
215  return true;
216 }
217 
219 {
220  mIsStaticFunc = isStatic;
221 }
222 
224 {
225  mIsStaticFunc = nullptr;
226  mIsStatic = isStatic;
227 }
228 
229 void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
230 {
231  mPrepareFunc = prepareFunc;
232 }
233 
235 {
236  if ( node && node->args() )
237  {
238  const QList< QgsExpressionNode * > argList = node->args()->list();
239  for ( QgsExpressionNode *argNode : argList )
240  {
241  if ( !argNode->isStatic( parent, context ) )
242  return false;
243  }
244  }
245 
246  return true;
247 }
248 
249 static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
250 {
251  double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
252  double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
253  double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
254 
255  if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
256  return QVariant();
257 
258  QVariantList array;
259  int length = 1;
260 
261  array << start;
262  double current = start + step;
263  while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
264  {
265  array << current;
266  current += step;
267  length++;
268  }
269 
270  return array;
271 }
272 
273 static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
274 {
275  if ( !context )
276  return QVariant();
277 
278  QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
279  return context->variable( name );
280 }
281 
282 static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
283 {
284  QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
285  return QgsExpression::replaceExpressionText( templateString, context );
286 }
287 
288 static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
289 {
290  if ( !context )
291  return QVariant();
292 
293  QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
294  QgsExpression expression( expString );
295  return expression.evaluate( context );
296 }
297 
298 static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
299 {
300  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
301  return QVariant( std::sqrt( x ) );
302 }
303 
304 static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
305 {
306  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
307  return QVariant( std::fabs( val ) );
308 }
309 
310 static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
311 {
312  double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
313  return ( deg * M_PI ) / 180;
314 }
315 static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
316 {
317  double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
318  return ( 180 * rad ) / M_PI;
319 }
320 static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
321 {
322  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
323  return QVariant( std::sin( x ) );
324 }
325 static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
326 {
327  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
328  return QVariant( std::cos( x ) );
329 }
330 static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
331 {
332  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
333  return QVariant( std::tan( x ) );
334 }
335 static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
336 {
337  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
338  return QVariant( std::asin( x ) );
339 }
340 static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
341 {
342  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
343  return QVariant( std::acos( x ) );
344 }
345 static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
346 {
347  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
348  return QVariant( std::atan( x ) );
349 }
350 static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
351 {
352  double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
353  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
354  return QVariant( std::atan2( y, x ) );
355 }
356 static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
357 {
358  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
359  return QVariant( std::exp( x ) );
360 }
361 static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
362 {
363  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
364  if ( x <= 0 )
365  return QVariant();
366  return QVariant( std::log( x ) );
367 }
368 static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
369 {
370  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
371  if ( x <= 0 )
372  return QVariant();
373  return QVariant( log10( x ) );
374 }
375 static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
376 {
377  double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
378  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
379  if ( x <= 0 || b <= 0 )
380  return QVariant();
381  return QVariant( std::log( x ) / std::log( b ) );
382 }
383 static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
384 {
385  double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
386  double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
387  if ( max < min )
388  return QVariant();
389 
390  std::random_device rd;
391  std::mt19937_64 generator( rd() );
392 
393  if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
394  {
395  quint32 seed;
396  if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
397  {
398  // if seed can be converted to int, we use as is
399  seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
400  }
401  else
402  {
403  // if not, we hash string representation to int
404  QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
405  std::hash<std::string> hasher;
406  seed = hasher( seedStr.toStdString() );
407  }
408  generator.seed( seed );
409  }
410 
411  // Return a random double in the range [min, max] (inclusive)
412  double f = static_cast< double >( generator() ) / static_cast< double >( generator.max() );
413  return QVariant( min + f * ( max - min ) );
414 }
415 static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
416 {
417  qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
418  qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
419  if ( max < min )
420  return QVariant();
421 
422  std::random_device rd;
423  std::mt19937_64 generator( rd() );
424 
425  if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
426  {
427  quint32 seed;
428  if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
429  {
430  // if seed can be converted to int, we use as is
431  seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
432  }
433  else
434  {
435  // if not, we hash string representation to int
436  QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
437  std::hash<std::string> hasher;
438  seed = hasher( seedStr.toStdString() );
439  }
440  generator.seed( seed );
441  }
442 
443  qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
444  if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
445  return QVariant( randomInteger );
446 
447  // Prevent wrong conversion of QVariant. See #36412
448  return QVariant( int( randomInteger ) );
449 }
450 
451 static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
452 {
453  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
454  double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
455  double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
456  double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
457  double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
458 
459  if ( domainMin >= domainMax )
460  {
461  parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
462  return QVariant();
463  }
464 
465  // outside of domain?
466  if ( val >= domainMax )
467  {
468  return rangeMax;
469  }
470  else if ( val <= domainMin )
471  {
472  return rangeMin;
473  }
474 
475  // calculate linear scale
476  double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
477  double c = rangeMin - ( domainMin * m );
478 
479  // Return linearly scaled value
480  return QVariant( m * val + c );
481 }
482 
483 static QVariant fcnExpScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
484 {
485  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
486  double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
487  double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
488  double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
489  double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
490  double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
491 
492  if ( domainMin >= domainMax )
493  {
494  parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
495  return QVariant();
496  }
497  if ( exponent <= 0 )
498  {
499  parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
500  return QVariant();
501  }
502 
503  // outside of domain?
504  if ( val >= domainMax )
505  {
506  return rangeMax;
507  }
508  else if ( val <= domainMin )
509  {
510  return rangeMin;
511  }
512 
513  // Return exponentially scaled value
514  return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
515 }
516 
517 static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
518 {
519  QVariant result( QVariant::Double );
520  double maxVal = std::numeric_limits<double>::quiet_NaN();
521  for ( const QVariant &val : values )
522  {
523  double testVal = val.isNull() ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
524  if ( std::isnan( maxVal ) )
525  {
526  maxVal = testVal;
527  }
528  else if ( !std::isnan( testVal ) )
529  {
530  maxVal = std::max( maxVal, testVal );
531  }
532  }
533 
534  if ( !std::isnan( maxVal ) )
535  {
536  result = QVariant( maxVal );
537  }
538  return result;
539 }
540 
541 static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
542 {
543  QVariant result( QVariant::Double );
544  double minVal = std::numeric_limits<double>::quiet_NaN();
545  for ( const QVariant &val : values )
546  {
547  double testVal = val.isNull() ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
548  if ( std::isnan( minVal ) )
549  {
550  minVal = testVal;
551  }
552  else if ( !std::isnan( testVal ) )
553  {
554  minVal = std::min( minVal, testVal );
555  }
556  }
557 
558  if ( !std::isnan( minVal ) )
559  {
560  result = QVariant( minVal );
561  }
562  return result;
563 }
564 
565 static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
566 {
567  //lazy eval, so we need to evaluate nodes now
568 
569  //first node is layer id or name
570  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
572  QVariant value = node->eval( parent, context );
574  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, parent );
575  if ( !vl )
576  {
577  parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
578  return QVariant();
579  }
580 
581  // second node is aggregate type
582  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
584  value = node->eval( parent, context );
586  bool ok = false;
587  QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
588  if ( !ok )
589  {
590  parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
591  return QVariant();
592  }
593 
594  // third node is subexpression (or field name)
595  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
597  QString subExpression = node->dump();
598 
600  //optional forth node is filter
601  if ( values.count() > 3 )
602  {
603  node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
605  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
606  if ( !nl || nl->value().isValid() )
607  parameters.filter = node->dump();
608  }
609 
610  //optional fifth node is concatenator
611  if ( values.count() > 4 )
612  {
613  node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
615  value = node->eval( parent, context );
617  parameters.delimiter = value.toString();
618  }
619 
620  //optional sixth node is order by
621  QString orderBy;
622  if ( values.count() > 5 )
623  {
624  node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
626  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
627  if ( !nl || nl->value().isValid() )
628  {
629  orderBy = node->dump();
630  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
631  }
632  }
633 
634  QVariant result;
635  if ( context )
636  {
637  QString cacheKey;
638  QgsExpression subExp( subExpression );
639  QgsExpression filterExp( parameters.filter );
640 
641  bool isStatic = true;
642  if ( filterExp.referencedVariables().contains( QStringLiteral( "parent" ) )
643  || filterExp.referencedVariables().contains( QString() )
644  || subExp.referencedVariables().contains( QStringLiteral( "parent" ) )
645  || subExp.referencedVariables().contains( QString() ) )
646  {
647  isStatic = false;
648  }
649  else
650  {
651  const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
652  for ( const QString &varName : refVars )
653  {
654  const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
655  if ( scope && !scope->isStatic( varName ) )
656  {
657  isStatic = false;
658  break;
659  }
660  }
661  }
662 
663  if ( !isStatic )
664  {
665  cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter,
666  QString::number( context->feature().id() ), QString( qHash( context->feature() ) ), orderBy );
667  }
668  else
669  {
670  cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter, orderBy );
671  }
672 
673  if ( context && context->hasCachedValue( cacheKey ) )
674  {
675  return context->cachedValue( cacheKey );
676  }
677 
678  QgsExpressionContext subContext( *context );
680  subScope->setVariable( QStringLiteral( "parent" ), context->feature() );
681  subContext.appendScope( subScope );
682  result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok );
683 
684  context->setCachedValue( cacheKey, result );
685  }
686  else
687  {
688  result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok );
689  }
690  if ( !ok )
691  {
692  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
693  return QVariant();
694  }
695 
696  return result;
697 }
698 
699 static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
700 {
701  if ( !context )
702  {
703  parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
704  return QVariant();
705  }
706 
707  // first step - find current layer
708  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
709  if ( !vl )
710  {
711  parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
712  return QVariant();
713  }
714 
715  //lazy eval, so we need to evaluate nodes now
716 
717  //first node is relation name
718  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
720  QVariant value = node->eval( parent, context );
722  QString relationId = value.toString();
723  // check relation exists
724  QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId );
725  if ( !relation.isValid() || relation.referencedLayer() != vl )
726  {
727  // check for relations by name
728  QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId );
729  if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
730  {
731  parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
732  return QVariant();
733  }
734  else
735  {
736  relation = relations.at( 0 );
737  }
738  }
739 
740  QgsVectorLayer *childLayer = relation.referencingLayer();
741 
742  // second node is aggregate type
743  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
745  value = node->eval( parent, context );
747  bool ok = false;
748  QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
749  if ( !ok )
750  {
751  parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
752  return QVariant();
753  }
754 
755  //third node is subexpression (or field name)
756  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
758  QString subExpression = node->dump();
759 
760  //optional fourth node is concatenator
762  if ( values.count() > 3 )
763  {
764  node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
766  value = node->eval( parent, context );
768  parameters.delimiter = value.toString();
769  }
770 
771  //optional fifth node is order by
772  QString orderBy;
773  if ( values.count() > 4 )
774  {
775  node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
777  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
778  if ( !nl || nl->value().isValid() )
779  {
780  orderBy = node->dump();
781  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
782  }
783  }
784 
785  if ( !context->hasFeature() )
786  return QVariant();
787  QgsFeature f = context->feature();
788 
789  parameters.filter = relation.getRelatedFeaturesFilter( f );
790 
791  QString cacheKey = QStringLiteral( "relagg:%1:%2:%3:%4:%5" ).arg( vl->id(),
792  QString::number( static_cast< int >( aggregate ) ),
793  subExpression,
794  parameters.filter,
795  orderBy );
796  if ( context->hasCachedValue( cacheKey ) )
797  return context->cachedValue( cacheKey );
798 
799  QVariant result;
800  ok = false;
801 
802 
803  QgsExpressionContext subContext( *context );
804  result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok );
805 
806  if ( !ok )
807  {
808  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
809  return QVariant();
810  }
811 
812  // cache value
813  context->setCachedValue( cacheKey, result );
814  return result;
815 }
816 
817 
818 static QVariant fcnAggregateGeneric( QgsAggregateCalculator::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
819 {
820  if ( !context )
821  {
822  parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
823  return QVariant();
824  }
825 
826  // first step - find current layer
827  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
828  if ( !vl )
829  {
830  parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
831  return QVariant();
832  }
833 
834  //lazy eval, so we need to evaluate nodes now
835 
836  //first node is subexpression (or field name)
837  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
839  QString subExpression = node->dump();
840 
841  //optional second node is group by
842  QString groupBy;
843  if ( values.count() > 1 )
844  {
845  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
847  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
848  if ( !nl || nl->value().isValid() )
849  groupBy = node->dump();
850  }
851 
852  //optional third node is filter
853  if ( values.count() > 2 )
854  {
855  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
857  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
858  if ( !nl || nl->value().isValid() )
859  parameters.filter = node->dump();
860  }
861 
862  //optional order by node, if supported
863  QString orderBy;
864  if ( orderByPos >= 0 && values.count() > orderByPos )
865  {
866  node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
868  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
869  if ( !nl || nl->value().isValid() )
870  {
871  orderBy = node->dump();
872  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
873  }
874  }
875 
876  // build up filter with group by
877 
878  // find current group by value
879  if ( !groupBy.isEmpty() )
880  {
881  QgsExpression groupByExp( groupBy );
882  QVariant groupByValue = groupByExp.evaluate( context );
883  QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
884  groupByValue.isNull() ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
885  QgsExpression::quotedValue( groupByValue ) );
886  if ( !parameters.filter.isEmpty() )
887  parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
888  else
889  parameters.filter = groupByClause;
890  }
891 
892  QgsExpression subExp( subExpression );
893  QgsExpression filterExp( parameters.filter );
894 
895  bool isStatic = true;
896  const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
897  for ( const QString &varName : refVars )
898  {
899  const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
900  if ( scope && !scope->isStatic( varName ) )
901  {
902  isStatic = false;
903  break;
904  }
905  }
906 
907  QString cacheKey;
908  if ( !isStatic )
909  {
910  cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter,
911  QString::number( context->feature().id() ), QString( qHash( context->feature() ) ), orderBy );
912  }
913  else
914  {
915  cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter, orderBy );
916  }
917 
918  if ( context->hasCachedValue( cacheKey ) )
919  return context->cachedValue( cacheKey );
920 
921  QVariant result;
922  bool ok = false;
923 
924  QgsExpressionContext subContext( *context );
926  subScope->setVariable( QStringLiteral( "parent" ), context->feature() );
927  subContext.appendScope( subScope );
928  result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok );
929 
930  if ( !ok )
931  {
932  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
933  return QVariant();
934  }
935 
936  // cache value
937  context->setCachedValue( cacheKey, result );
938  return result;
939 }
940 
941 
942 static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
943 {
944  return fcnAggregateGeneric( QgsAggregateCalculator::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
945 }
946 
947 static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
948 {
949  return fcnAggregateGeneric( QgsAggregateCalculator::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
950 }
951 
952 static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
953 {
954  return fcnAggregateGeneric( QgsAggregateCalculator::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
955 }
956 
957 static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
958 {
959  return fcnAggregateGeneric( QgsAggregateCalculator::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
960 }
961 
962 static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
963 {
964  return fcnAggregateGeneric( QgsAggregateCalculator::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
965 }
966 
967 static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
968 {
969  return fcnAggregateGeneric( QgsAggregateCalculator::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
970 }
971 
972 static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
973 {
974  return fcnAggregateGeneric( QgsAggregateCalculator::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
975 }
976 
977 static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
978 {
979  return fcnAggregateGeneric( QgsAggregateCalculator::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
980 }
981 
982 static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
983 {
984  return fcnAggregateGeneric( QgsAggregateCalculator::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
985 }
986 
987 static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
988 {
989  return fcnAggregateGeneric( QgsAggregateCalculator::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
990 }
991 
992 static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
993 {
994  return fcnAggregateGeneric( QgsAggregateCalculator::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
995 }
996 
997 static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
998 {
999  return fcnAggregateGeneric( QgsAggregateCalculator::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1000 }
1001 
1002 static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1003 {
1004  return fcnAggregateGeneric( QgsAggregateCalculator::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1005 }
1006 
1007 static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1008 {
1009  return fcnAggregateGeneric( QgsAggregateCalculator::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1010 }
1011 
1012 static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1013 {
1014  return fcnAggregateGeneric( QgsAggregateCalculator::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1015 }
1016 
1017 static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1018 {
1019  return fcnAggregateGeneric( QgsAggregateCalculator::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1020 }
1021 
1022 static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1023 {
1024  return fcnAggregateGeneric( QgsAggregateCalculator::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1025 }
1026 
1027 static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1028 {
1029  return fcnAggregateGeneric( QgsAggregateCalculator::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1030 }
1031 
1032 static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1033 {
1035 
1036  //fourth node is concatenator
1037  if ( values.count() > 3 )
1038  {
1039  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1041  QVariant value = node->eval( parent, context );
1043  parameters.delimiter = value.toString();
1044  }
1045 
1046  return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenate, values, parameters, context, parent, 4 );
1047 }
1048 
1049 static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1050 {
1052 
1053  //fourth node is concatenator
1054  if ( values.count() > 3 )
1055  {
1056  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1058  QVariant value = node->eval( parent, context );
1060  parameters.delimiter = value.toString();
1061  }
1062 
1063  return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenateUnique, values, parameters, context, parent, 4 );
1064 }
1065 
1066 static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1067 {
1068  return fcnAggregateGeneric( QgsAggregateCalculator::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1069 }
1070 
1071 static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1072 {
1073  if ( !context )
1074  return QVariant();
1075 
1076  QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1077  bool ok = false;
1078  if ( !scale.isValid() || scale.isNull() )
1079  return QVariant();
1080 
1081  const double v = scale.toDouble( &ok );
1082  if ( ok )
1083  return v;
1084  return QVariant();
1085 }
1086 
1087 static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1088 {
1089  double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1090  double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1091  double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1092 
1093  // force testValue to sit inside the range specified by the min and max value
1094  if ( testValue <= minValue )
1095  {
1096  return QVariant( minValue );
1097  }
1098  else if ( testValue >= maxValue )
1099  {
1100  return QVariant( maxValue );
1101  }
1102  else
1103  {
1104  return QVariant( testValue );
1105  }
1106 }
1107 
1108 static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1109 {
1110  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1111  return QVariant( std::floor( x ) );
1112 }
1113 
1114 static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1115 {
1116  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1117  return QVariant( std::ceil( x ) );
1118 }
1119 
1120 static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1121 {
1122  return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1123 }
1124 static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1125 {
1126  return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1127 }
1128 static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1129 {
1130  return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1131 }
1132 
1133 static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1134 {
1135  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1136  QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1137  if ( format.isEmpty() && !language.isEmpty() )
1138  {
1139  parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1140  return QVariant( QDateTime() );
1141  }
1142 
1143  if ( format.isEmpty() && language.isEmpty() )
1144  return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1145 
1146  QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1147  QLocale locale = QLocale();
1148  if ( !language.isEmpty() )
1149  {
1150  locale = QLocale( language );
1151  }
1152 
1153  QDateTime datetime = locale.toDateTime( datetimestring, format );
1154  if ( !datetime.isValid() )
1155  {
1156  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1157  datetime = QDateTime();
1158  }
1159  return QVariant( datetime );
1160 }
1161 
1162 static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1163 {
1164  const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1165  const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1166  const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1167 
1168  const QDate date( year, month, day );
1169  if ( !date.isValid() )
1170  {
1171  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1172  return QVariant();
1173  }
1174  return QVariant( date );
1175 }
1176 
1177 static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1178 {
1179  const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1180  const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1181  const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1182 
1183  const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1184  if ( !time.isValid() )
1185  {
1186  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1187  return QVariant();
1188  }
1189  return QVariant( time );
1190 }
1191 
1192 static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1193 {
1194  const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1195  const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1196  const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1197  const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1198  const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1199  const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1200 
1201  const QDate date( year, month, day );
1202  if ( !date.isValid() )
1203  {
1204  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1205  return QVariant();
1206  }
1207  const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1208  if ( !time.isValid() )
1209  {
1210  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1211  return QVariant();
1212  }
1213  return QVariant( QDateTime( date, time ) );
1214 }
1215 
1216 static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1217 {
1218  const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1219  const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1220  const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1221  const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1222  const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1223  const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1224  const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1225 
1226  return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1227 }
1228 
1229 static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1230 {
1231  for ( const QVariant &value : values )
1232  {
1233  if ( value.isNull() )
1234  continue;
1235  return value;
1236  }
1237  return QVariant();
1238 }
1239 
1240 static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1241 {
1242  const QVariant val1 = values.at( 0 );
1243  const QVariant val2 = values.at( 1 );
1244 
1245  if ( val1 == val2 )
1246  return QVariant();
1247  else
1248  return val1;
1249 }
1250 
1251 static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1252 {
1253  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1254  return QVariant( str.toLower() );
1255 }
1256 static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1257 {
1258  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1259  return QVariant( str.toUpper() );
1260 }
1261 static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1262 {
1263  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1264  QStringList elems = str.split( ' ' );
1265  for ( int i = 0; i < elems.size(); i++ )
1266  {
1267  if ( elems[i].size() > 1 )
1268  elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1269  }
1270  return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1271 }
1272 
1273 static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1274 {
1275  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1276  return QVariant( str.trimmed() );
1277 }
1278 
1279 static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1280 {
1281  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1282  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1283  return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1284 }
1285 
1286 static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1287 {
1288  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1289  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1290  return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1291 }
1292 
1293 static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1294 {
1295  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1296  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1297  int dist = QgsStringUtils::hammingDistance( string1, string2 );
1298  return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1299 }
1300 
1301 static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1302 {
1303  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1304  return QVariant( QgsStringUtils::soundex( string ) );
1305 }
1306 
1307 static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1308 {
1309  QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1310  return QVariant( QString( character ) );
1311 }
1312 
1313 static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1314 {
1315  QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1316 
1317  if ( value.isEmpty() )
1318  {
1319  return QVariant();
1320  }
1321 
1322  int res = value.at( 0 ).unicode();
1323  return QVariant( res );
1324 }
1325 
1326 static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1327 {
1328  if ( values.length() == 2 || values.length() == 3 )
1329  {
1330  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1331  qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1332 
1333  QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1334 
1335  return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1336  }
1337 
1338  return QVariant();
1339 }
1340 
1341 static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1342 {
1343  // two variants, one for geometry, one for string
1344  if ( values.at( 0 ).canConvert<QgsGeometry>() )
1345  {
1346  //geometry variant
1347  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1348  if ( geom.type() != QgsWkbTypes::LineGeometry )
1349  return QVariant();
1350 
1351  return QVariant( geom.length() );
1352  }
1353 
1354  //otherwise fall back to string variant
1355  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1356  return QVariant( str.length() );
1357 }
1358 
1359 static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1360 {
1361  if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
1362  {
1363  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1364  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1365 
1366  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1367  {
1368  str = str.replace( it.key(), it.value().toString() );
1369  }
1370 
1371  return QVariant( str );
1372  }
1373  else if ( values.count() == 3 )
1374  {
1375  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1376  QVariantList before;
1377  QVariantList after;
1378  bool isSingleReplacement = false;
1379 
1380  if ( values.at( 1 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
1381  {
1382  before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1383  }
1384  else
1385  {
1386  before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1387  }
1388 
1389  if ( values.at( 2 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
1390  {
1391  after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1392  isSingleReplacement = true;
1393  }
1394  else
1395  {
1396  after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1397  }
1398 
1399  if ( !isSingleReplacement && before.length() != after.length() )
1400  {
1401  parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1402  return QVariant();
1403  }
1404 
1405  for ( int i = 0; i < before.length(); i++ )
1406  {
1407  str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1408  }
1409 
1410  return QVariant( str );
1411  }
1412  else
1413  {
1414  parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1415  return QVariant();
1416  }
1417 }
1418 static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1419 {
1420  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1421  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1422  QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1423 
1424  QRegularExpression re( regexp );
1425  if ( !re.isValid() )
1426  {
1427  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1428  return QVariant();
1429  }
1430  return QVariant( str.replace( re, after ) );
1431 }
1432 
1433 static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1434 {
1435  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1436  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1437 
1438  QRegularExpression re( regexp );
1439  if ( !re.isValid() )
1440  {
1441  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1442  return QVariant();
1443  }
1444  return QVariant( ( str.indexOf( re ) + 1 ) );
1445 }
1446 
1447 static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1448 {
1449  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1450  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1451  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1452 
1453  QRegularExpression re( regexp );
1454  if ( !re.isValid() )
1455  {
1456  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1457  return QVariant();
1458  }
1459 
1460  QRegularExpressionMatch matches = re.match( str );
1461  if ( matches.hasMatch() )
1462  {
1463  QVariantList array;
1464  QStringList list = matches.capturedTexts();
1465 
1466  // Skip the first string to only return captured groups
1467  for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1468  {
1469  array += ( !( *it ).isEmpty() ) ? *it : empty;
1470  }
1471 
1472  return QVariant( array );
1473  }
1474  else
1475  {
1476  return QVariant();
1477  }
1478 }
1479 
1480 static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1481 {
1482  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1483  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1484 
1485  QRegularExpression re( regexp );
1486  if ( !re.isValid() )
1487  {
1488  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1489  return QVariant();
1490  }
1491 
1492  // extract substring
1493  QRegularExpressionMatch match = re.match( str );
1494  if ( match.hasMatch() )
1495  {
1496  // return first capture
1497  if ( match.lastCapturedIndex() > 0 )
1498  {
1499  // a capture group was present, so use that
1500  return QVariant( match.captured( 1 ) );
1501  }
1502  else
1503  {
1504  // no capture group, so using all match
1505  return QVariant( match.captured( 0 ) );
1506  }
1507  }
1508  else
1509  {
1510  return QVariant( "" );
1511  }
1512 }
1513 
1514 static QVariant fcnUuid( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1515 {
1516  return QUuid::createUuid().toString();
1517 }
1518 
1519 static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1520 {
1521  if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1522  return QVariant();
1523 
1524  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1525  int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1526 
1527  int len = 0;
1528  if ( values.at( 2 ).isValid() )
1529  len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1530  else
1531  len = str.size();
1532 
1533  if ( from < 0 )
1534  {
1535  from = str.size() + from;
1536  if ( from < 0 )
1537  {
1538  from = 0;
1539  }
1540  }
1541  else if ( from > 0 )
1542  {
1543  //account for the fact that substr() starts at 1
1544  from -= 1;
1545  }
1546 
1547  if ( len < 0 )
1548  {
1549  len = str.size() + len - from;
1550  if ( len < 0 )
1551  {
1552  len = 0;
1553  }
1554  }
1555 
1556  return QVariant( str.mid( from, len ) );
1557 }
1558 static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1559 {
1560  FEAT_FROM_CONTEXT( context, f )
1561  // TODO: handling of 64-bit feature ids?
1562  return QVariant( static_cast< int >( f.id() ) );
1563 }
1564 
1565 static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1566 {
1567  QgsRasterLayer *layer = QgsExpressionUtils::getRasterLayer( values.at( 0 ), parent );
1568  if ( !layer || !layer->dataProvider() )
1569  {
1570  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1571  return QVariant();
1572  }
1573 
1574  int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1575  if ( bandNb < 1 || bandNb > layer->bandCount() )
1576  {
1577  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1578  return QVariant();
1579  }
1580 
1581  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1582  if ( geom.isNull() || geom.type() != QgsWkbTypes::PointGeometry )
1583  {
1584  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1585  return QVariant();
1586  }
1587 
1588  QgsPointXY point = geom.asPoint();
1589  if ( geom.isMultipart() )
1590  {
1591  QgsMultiPointXY multiPoint = geom.asMultiPoint();
1592  if ( multiPoint.count() == 1 )
1593  {
1594  point = multiPoint[0];
1595  }
1596  else
1597  {
1598  // if the geometry contains more than one part, return an undefined value
1599  return QVariant();
1600  }
1601  }
1602 
1603  double value = layer->dataProvider()->sample( point, bandNb );
1604  return std::isnan( value ) ? QVariant() : value;
1605 }
1606 
1607 static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1608 {
1609  if ( !context )
1610  return QVariant();
1611 
1612  return context->feature();
1613 }
1614 
1615 static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1616 {
1617  QgsFeature feature;
1618  QString attr;
1619  if ( values.size() == 1 )
1620  {
1621  attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1622  feature = context->feature();
1623  }
1624  else if ( values.size() == 2 )
1625  {
1626  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1627  attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1628  }
1629  else
1630  {
1631  parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %1 given." ).arg( values.length() ) );
1632  return QVariant();
1633  }
1634 
1635  return feature.attribute( attr );
1636 }
1637 
1638 static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1639 {
1640  QgsFeature feature;
1641  if ( values.size() == 0 || values.at( 0 ).isNull() )
1642  {
1643  feature = context->feature();
1644  }
1645  else
1646  {
1647  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1648  }
1649 
1650  const QgsFields fields = feature.fields();
1651  QVariantMap result;
1652  for ( int i = 0; i < fields.count(); ++i )
1653  {
1654  result.insert( fields.at( i ).name(), feature.attribute( i ) );
1655  }
1656  return result;
1657 }
1658 
1659 static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
1660 {
1661  QgsVectorLayer *layer = nullptr;
1662  QgsFeature feature;
1663  bool evaluate = true;
1664 
1665  if ( values.isEmpty() )
1666  {
1667  feature = context->feature();
1668  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1669  }
1670  else if ( values.size() == 1 )
1671  {
1672  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1673  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1674  }
1675  else if ( values.size() == 2 )
1676  {
1677  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1678  feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1679  }
1680  else if ( values.size() == 3 )
1681  {
1682  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1683  feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1684  evaluate = values.value( 2 ).toBool();
1685  }
1686  else
1687  {
1688  if ( isMaptip )
1689  {
1690  parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %1 given." ).arg( values.length() ) );
1691  }
1692  else
1693  {
1694  parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %1 given." ).arg( values.length() ) );
1695  }
1696  return QVariant();
1697  }
1698 
1699  if ( !layer )
1700  {
1701  parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
1702  return QVariant( );
1703  }
1704 
1705  if ( !feature.isValid() )
1706  {
1707  parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
1708  return QVariant( );
1709  }
1710 
1711  if ( ! evaluate )
1712  {
1713  if ( isMaptip )
1714  {
1715  return layer->mapTipTemplate();
1716  }
1717  else
1718  {
1719  return layer->displayExpression();
1720  }
1721  }
1722 
1723  QgsExpressionContext subContext( *context );
1724  subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
1725  subContext.setFeature( feature );
1726 
1727  if ( isMaptip )
1728  {
1729  return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
1730  }
1731  else
1732  {
1733  QgsExpression exp( layer->displayExpression() );
1734  exp.prepare( &subContext );
1735  return exp.evaluate( &subContext ).toString();
1736  }
1737 }
1738 
1739 static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1740 {
1741  return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
1742 }
1743 
1744 static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1745 {
1746  return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
1747 }
1748 
1749 static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1750 {
1751  QgsVectorLayer *layer = nullptr;
1752  QgsFeature feature;
1753 
1754  if ( values.isEmpty() )
1755  {
1756  feature = context->feature();
1757  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1758  }
1759  else if ( values.size() == 1 )
1760  {
1761  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1762  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1763  }
1764  else if ( values.size() == 2 )
1765  {
1766  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1767  feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1768  }
1769  else
1770  {
1771  parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %1 given." ).arg( values.length() ) );
1772  return QVariant();
1773  }
1774 
1775  if ( !layer || !feature.isValid() )
1776  {
1777  return QVariant( QVariant::Bool );
1778  }
1779 
1780  return layer->selectedFeatureIds().contains( feature.id() );
1781 }
1782 
1783 static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1784 {
1785  QgsVectorLayer *layer = nullptr;
1786 
1787  if ( values.isEmpty() )
1788  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1789  else if ( values.count() == 1 )
1790  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1791  else
1792  {
1793  parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %1 given." ).arg( values.length() ) );
1794  return QVariant();
1795  }
1796 
1797  if ( !layer )
1798  {
1799  return QVariant( QVariant::LongLong );
1800  }
1801 
1802  return layer->selectedFeatureCount();
1803 }
1804 
1805 static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1806 {
1807  static QMap<QString, qlonglong> counterCache;
1808  QVariant functionResult;
1809 
1810  std::function<void()> fetchAndIncrementFunc = [ =, &functionResult ]()
1811  {
1812  QString database;
1813  const QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1814 
1815  if ( layer )
1816  {
1817  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
1818  database = decodedUri.value( QStringLiteral( "path" ) ).toString();
1819  if ( database.isEmpty() )
1820  {
1821  parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
1822  }
1823  }
1824  else
1825  {
1826  database = values.at( 0 ).toString();
1827  }
1828 
1829  const QString table = values.at( 1 ).toString();
1830  const QString idColumn = values.at( 2 ).toString();
1831  const QString filterAttribute = values.at( 3 ).toString();
1832  const QVariant filterValue = values.at( 4 ).toString();
1833  const QVariantMap defaultValues = values.at( 5 ).toMap();
1834 
1835  // read from database
1836  sqlite3_database_unique_ptr sqliteDb;
1837  sqlite3_statement_unique_ptr sqliteStatement;
1838 
1839  if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
1840  {
1841  parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
1842  functionResult = QVariant();
1843  return;
1844  }
1845 
1846  QString errorMessage;
1847  QString currentValSql;
1848 
1849  qlonglong nextId = 0;
1850  bool cachedMode = false;
1851  bool valueRetrieved = false;
1852 
1853  QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
1854 
1855  // Running in transaction mode, check for cached value first
1856  if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
1857  {
1858  cachedMode = true;
1859 
1860  auto cachedCounter = counterCache.find( cacheString );
1861 
1862  if ( cachedCounter != counterCache.end() )
1863  {
1864  qlonglong &cachedValue = cachedCounter.value();
1865  nextId = cachedValue;
1866  nextId += 1;
1867  cachedValue = nextId;
1868  valueRetrieved = true;
1869  }
1870  }
1871 
1872  // Either not in cached mode or no cached value found, obtain from DB
1873  if ( !cachedMode || !valueRetrieved )
1874  {
1875  int result = SQLITE_ERROR;
1876 
1877  currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
1878  if ( !filterAttribute.isNull() )
1879  {
1880  currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
1881  }
1882 
1883  sqliteStatement = sqliteDb.prepare( currentValSql, result );
1884 
1885  if ( result == SQLITE_OK )
1886  {
1887  nextId = 0;
1888  if ( sqliteStatement.step() == SQLITE_ROW )
1889  {
1890  nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
1891  }
1892 
1893  // If in cached mode: add value to cache and connect to transaction
1894  if ( cachedMode && result == SQLITE_OK )
1895  {
1896  counterCache.insert( cacheString, nextId );
1897 
1898  QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
1899  {
1900  counterCache.remove( cacheString );
1901  } );
1902  }
1903  valueRetrieved = true;
1904  }
1905  }
1906 
1907  if ( valueRetrieved )
1908  {
1909  QString upsertSql;
1910  upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
1911  QStringList cols;
1912  QStringList vals;
1913  cols << QgsSqliteUtils::quotedIdentifier( idColumn );
1914  vals << QgsSqliteUtils::quotedValue( nextId );
1915 
1916  if ( !filterAttribute.isNull() )
1917  {
1918  cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
1919  vals << QgsSqliteUtils::quotedValue( filterValue );
1920  }
1921 
1922  for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
1923  {
1924  cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
1925  vals << iter.value().toString();
1926  }
1927 
1928  upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
1929  upsertSql += QLatin1String( " VALUES " );
1930  upsertSql += '(' + vals.join( ',' ) + ')';
1931 
1932  int result = SQLITE_ERROR;
1933  if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
1934  {
1935  QgsTransaction *transaction = layer->dataProvider()->transaction();
1936  if ( transaction->executeSql( upsertSql, errorMessage ) )
1937  {
1938  result = SQLITE_OK;
1939  }
1940  }
1941  else
1942  {
1943  result = sqliteDb.exec( upsertSql, errorMessage );
1944  }
1945  if ( result == SQLITE_OK )
1946  {
1947  functionResult = QVariant( nextId );
1948  return;
1949  }
1950  else
1951  {
1952  parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
1953  functionResult = QVariant();
1954  return;
1955  }
1956  }
1957 
1958  functionResult = QVariant();
1959  };
1960 
1961  QgsThreadingUtils::runOnMainThread( fetchAndIncrementFunc );
1962 
1963  return functionResult;
1964 }
1965 
1966 static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1967 {
1968  QString concat;
1969  for ( const QVariant &value : values )
1970  {
1971  if ( !value.isNull() )
1972  concat += QgsExpressionUtils::getStringValue( value, parent );
1973  }
1974  return concat;
1975 }
1976 
1977 static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1978 {
1979  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1980  return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
1981 }
1982 
1983 static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1984 {
1985  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1986  int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1987  return string.right( pos );
1988 }
1989 
1990 static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1991 {
1992  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1993  int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1994  return string.left( pos );
1995 }
1996 
1997 static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1998 {
1999  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2000  int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2001  QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2002  return string.leftJustified( length, fill.at( 0 ), true );
2003 }
2004 
2005 static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2006 {
2007  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2008  int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2009  QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2010  return string.rightJustified( length, fill.at( 0 ), true );
2011 }
2012 
2013 static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2014 {
2015  if ( values.size() < 1 )
2016  {
2017  parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2018  return QVariant();
2019  }
2020 
2021  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2022  for ( int n = 1; n < values.length(); n++ )
2023  {
2024  string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2025  }
2026  return string;
2027 }
2028 
2029 
2030 static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2031 {
2032  return QVariant( QDateTime::currentDateTime() );
2033 }
2034 
2035 static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2036 {
2037  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2038  QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2039  if ( format.isEmpty() && !language.isEmpty() )
2040  {
2041  parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2042  return QVariant( QDate() );
2043  }
2044 
2045  if ( format.isEmpty() && language.isEmpty() )
2046  return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2047 
2048  QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2049  QLocale locale = QLocale();
2050  if ( !language.isEmpty() )
2051  {
2052  locale = QLocale( language );
2053  }
2054 
2055  QDate date = locale.toDate( datestring, format );
2056  if ( !date.isValid() )
2057  {
2058  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2059  date = QDate();
2060  }
2061  return QVariant( date );
2062 }
2063 
2064 static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2065 {
2066  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2067  QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2068  if ( format.isEmpty() && !language.isEmpty() )
2069  {
2070  parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2071  return QVariant( QTime() );
2072  }
2073 
2074  if ( format.isEmpty() && language.isEmpty() )
2075  return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2076 
2077  QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2078  QLocale locale = QLocale();
2079  if ( !language.isEmpty() )
2080  {
2081  locale = QLocale( language );
2082  }
2083 
2084  QTime time = locale.toTime( timestring, format );
2085  if ( !time.isValid() )
2086  {
2087  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2088  time = QTime();
2089  }
2090  return QVariant( time );
2091 }
2092 
2093 static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2094 {
2095  return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2096 }
2097 
2098 /*
2099  * DMS functions
2100  */
2101 
2102 static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2103 {
2104  double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2105  QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2106  int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2107 
2108  QString formatString;
2109  if ( values.count() > 3 )
2110  formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2111 
2112  QgsCoordinateFormatter::FormatFlags flags = QgsCoordinateFormatter::FormatFlags();
2113  if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2114  {
2116  }
2117  else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2118  {
2120  }
2121  else if ( ! formatString.isEmpty() )
2122  {
2123  parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2124  return QVariant();
2125  }
2126 
2127  if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2128  {
2129  return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2130  }
2131  else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2132  {
2133  return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2134  }
2135  else
2136  {
2137  parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2138  return QVariant();
2139  }
2140 }
2141 
2142 static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2143 {
2145  return floatToDegreeFormat( format, values, context, parent, node );
2146 }
2147 
2148 static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2149 {
2150  double value = 0.0;
2151  bool ok = false;
2152  value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2153 
2154  return ok ? QVariant( value ) : QVariant();
2155 }
2156 
2157 static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2158 {
2160  return floatToDegreeFormat( format, values, context, parent, node );
2161 }
2162 
2163 static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2164 {
2165  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2166  QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2167  qint64 seconds = d2.secsTo( d1 );
2168  return QVariant::fromValue( QgsInterval( seconds ) );
2169 }
2170 
2171 static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2172 {
2173  if ( !values.at( 0 ).canConvert<QDate>() )
2174  return QVariant();
2175 
2176  QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2177  if ( !date.isValid() )
2178  return QVariant();
2179 
2180  // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2181  // (to match PostgreSQL behavior)
2182  return date.dayOfWeek() % 7;
2183 }
2184 
2185 static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2186 {
2187  QVariant value = values.at( 0 );
2188  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2189  if ( inter.isValid() )
2190  {
2191  return QVariant( inter.days() );
2192  }
2193  else
2194  {
2195  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2196  return QVariant( d1.date().day() );
2197  }
2198 }
2199 
2200 static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2201 {
2202  QVariant value = values.at( 0 );
2203  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2204  if ( inter.isValid() )
2205  {
2206  return QVariant( inter.years() );
2207  }
2208  else
2209  {
2210  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2211  return QVariant( d1.date().year() );
2212  }
2213 }
2214 
2215 static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2216 {
2217  QVariant value = values.at( 0 );
2218  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2219  if ( inter.isValid() )
2220  {
2221  return QVariant( inter.months() );
2222  }
2223  else
2224  {
2225  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2226  return QVariant( d1.date().month() );
2227  }
2228 }
2229 
2230 static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2231 {
2232  QVariant value = values.at( 0 );
2233  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2234  if ( inter.isValid() )
2235  {
2236  return QVariant( inter.weeks() );
2237  }
2238  else
2239  {
2240  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2241  return QVariant( d1.date().weekNumber() );
2242  }
2243 }
2244 
2245 static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2246 {
2247  QVariant value = values.at( 0 );
2248  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2249  if ( inter.isValid() )
2250  {
2251  return QVariant( inter.hours() );
2252  }
2253  else
2254  {
2255  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2256  return QVariant( t1.hour() );
2257  }
2258 }
2259 
2260 static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2261 {
2262  QVariant value = values.at( 0 );
2263  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2264  if ( inter.isValid() )
2265  {
2266  return QVariant( inter.minutes() );
2267  }
2268  else
2269  {
2270  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2271  return QVariant( t1.minute() );
2272  }
2273 }
2274 
2275 static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2276 {
2277  QVariant value = values.at( 0 );
2278  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2279  if ( inter.isValid() )
2280  {
2281  return QVariant( inter.seconds() );
2282  }
2283  else
2284  {
2285  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2286  return QVariant( t1.second() );
2287  }
2288 }
2289 
2290 static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2291 {
2292  QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2293  if ( dt.isValid() )
2294  {
2295  return QVariant( dt.toMSecsSinceEpoch() );
2296  }
2297  else
2298  {
2299  return QVariant();
2300  }
2301 }
2302 
2303 static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2304 {
2305  long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2306  // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2307  return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2308 }
2309 
2310 #define ENSURE_GEOM_TYPE(f, g, geomtype) \
2311  if ( !(f).hasGeometry() ) \
2312  return QVariant(); \
2313  QgsGeometry g = (f).geometry(); \
2314  if ( (g).type() != (geomtype) ) \
2315  return QVariant();
2316 
2317 static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2318 {
2319  FEAT_FROM_CONTEXT( context, f )
2321  if ( g.isMultipart() )
2322  {
2323  return g.asMultiPoint().at( 0 ).x();
2324  }
2325  else
2326  {
2327  return g.asPoint().x();
2328  }
2329 }
2330 
2331 static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2332 {
2333  FEAT_FROM_CONTEXT( context, f )
2335  if ( g.isMultipart() )
2336  {
2337  return g.asMultiPoint().at( 0 ).y();
2338  }
2339  else
2340  {
2341  return g.asPoint().y();
2342  }
2343 }
2344 
2345 static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2346 {
2347  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2348  if ( geom.isNull() )
2349  return QVariant();
2350 
2351  bool isValid = geom.isGeosValid();
2352 
2353  return QVariant( isValid );
2354 }
2355 
2356 static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2357 {
2358  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2359  if ( geom.isNull() )
2360  return QVariant();
2361 
2362  //if single point, return the point's x coordinate
2363  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2364  {
2365  return geom.asPoint().x();
2366  }
2367 
2368  //otherwise return centroid x
2369  QgsGeometry centroid = geom.centroid();
2370  QVariant result( centroid.asPoint().x() );
2371  return result;
2372 }
2373 
2374 static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2375 {
2376  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2377  if ( geom.isNull() )
2378  return QVariant();
2379 
2380  //if single point, return the point's y coordinate
2381  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2382  {
2383  return geom.asPoint().y();
2384  }
2385 
2386  //otherwise return centroid y
2387  QgsGeometry centroid = geom.centroid();
2388  QVariant result( centroid.asPoint().y() );
2389  return result;
2390 }
2391 
2392 static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2393 {
2394  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2395  if ( geom.isNull() )
2396  return QVariant(); //or 0?
2397 
2398  if ( !geom.constGet()->is3D() )
2399  return QVariant();
2400 
2401  //if single point, return the point's z coordinate
2402  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2403  {
2404  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2405  if ( point )
2406  return point->z();
2407  }
2408  else if ( geom.type() == QgsWkbTypes::PointGeometry && geom.isMultipart() )
2409  {
2410  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2411  {
2412  if ( collection->numGeometries() == 1 )
2413  {
2414  if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2415  return point->z();
2416  }
2417  }
2418  }
2419 
2420  return QVariant();
2421 }
2422 
2423 static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2424 {
2425  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2426  if ( geom.isNull() )
2427  return QVariant(); //or 0?
2428 
2429  if ( !geom.constGet()->isMeasure() )
2430  return QVariant();
2431 
2432  //if single point, return the point's m value
2433  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2434  {
2435  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2436  if ( point )
2437  return point->m();
2438  }
2439  else if ( geom.type() == QgsWkbTypes::PointGeometry && geom.isMultipart() )
2440  {
2441  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2442  {
2443  if ( collection->numGeometries() == 1 )
2444  {
2445  if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2446  return point->m();
2447  }
2448  }
2449  }
2450 
2451  return QVariant();
2452 }
2453 
2454 static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2455 {
2456  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2457 
2458  if ( geom.isNull() )
2459  return QVariant();
2460 
2461  int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2462 
2463  if ( idx < 0 )
2464  {
2465  //negative idx
2466  int count = geom.constGet()->nCoordinates();
2467  idx = count + idx;
2468  }
2469  else
2470  {
2471  //positive idx is 1 based
2472  idx -= 1;
2473  }
2474 
2475  QgsVertexId vId;
2476  if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
2477  {
2478  parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
2479  return QVariant();
2480  }
2481 
2482  QgsPoint point = geom.constGet()->vertexAt( vId );
2483  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2484 }
2485 
2486 static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2487 {
2488  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2489 
2490  if ( geom.isNull() )
2491  return QVariant();
2492 
2493  QgsVertexId vId;
2494  if ( !geom.vertexIdFromVertexNr( 0, vId ) )
2495  {
2496  return QVariant();
2497  }
2498 
2499  QgsPoint point = geom.constGet()->vertexAt( vId );
2500  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2501 }
2502 
2503 static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2504 {
2505  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2506 
2507  if ( geom.isNull() )
2508  return QVariant();
2509 
2510  QgsVertexId vId;
2511  if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
2512  {
2513  return QVariant();
2514  }
2515 
2516  QgsPoint point = geom.constGet()->vertexAt( vId );
2517  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2518 }
2519 
2520 static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2521 {
2522  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2523 
2524  if ( geom.isNull() )
2525  return QVariant();
2526 
2527  bool ignoreClosing = false;
2528  if ( values.length() > 1 )
2529  {
2530  ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
2531  }
2532 
2533  QgsMultiPoint *mp = new QgsMultiPoint();
2534 
2535  const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
2536  for ( const QgsRingSequence &part : sequence )
2537  {
2538  for ( const QgsPointSequence &ring : part )
2539  {
2540  bool skipLast = false;
2541  if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
2542  {
2543  skipLast = true;
2544  }
2545 
2546  for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
2547  {
2548  mp->addGeometry( ring.at( i ).clone() );
2549  }
2550  }
2551  }
2552 
2553  return QVariant::fromValue( QgsGeometry( mp ) );
2554 }
2555 
2556 static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2557 {
2558  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2559 
2560  if ( geom.isNull() )
2561  return QVariant();
2562 
2563  const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
2564 
2565  //OK, now we have a complete list of segmentized lines from the geometry
2567  for ( QgsLineString *line : linesToProcess )
2568  {
2569  for ( int i = 0; i < line->numPoints() - 1; ++i )
2570  {
2571  QgsLineString *segment = new QgsLineString();
2572  segment->setPoints( QgsPointSequence()
2573  << line->pointN( i )
2574  << line->pointN( i + 1 ) );
2575  ml->addGeometry( segment );
2576  }
2577  delete line;
2578  }
2579 
2580  return QVariant::fromValue( QgsGeometry( ml ) );
2581 }
2582 
2583 static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2584 {
2585  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2586 
2587  if ( geom.isNull() )
2588  return QVariant();
2589 
2590  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
2591  if ( !curvePolygon && geom.isMultipart() )
2592  {
2593  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2594  {
2595  if ( collection->numGeometries() == 1 )
2596  {
2597  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
2598  }
2599  }
2600  }
2601 
2602  if ( !curvePolygon )
2603  return QVariant();
2604 
2605  //idx is 1 based
2606  qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
2607 
2608  if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
2609  return QVariant();
2610 
2611  QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
2612  QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
2613  return result;
2614 }
2615 
2616 static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2617 {
2618  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2619 
2620  if ( geom.isNull() )
2621  return QVariant();
2622 
2623  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
2624  if ( !collection )
2625  return QVariant();
2626 
2627  //idx is 1 based
2628  qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
2629 
2630  if ( idx < 0 || idx >= collection->numGeometries() )
2631  return QVariant();
2632 
2633  QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
2634  QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
2635  return result;
2636 }
2637 
2638 static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2639 {
2640  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2641 
2642  if ( geom.isNull() )
2643  return QVariant();
2644 
2645  QgsAbstractGeometry *boundary = geom.constGet()->boundary();
2646  if ( !boundary )
2647  return QVariant();
2648 
2649  return QVariant::fromValue( QgsGeometry( boundary ) );
2650 }
2651 
2652 static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2653 {
2654  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2655 
2656  if ( geom.isNull() )
2657  return QVariant();
2658 
2659  QgsGeometry merged = geom.mergeLines();
2660  if ( merged.isNull() )
2661  return QVariant();
2662 
2663  return QVariant::fromValue( merged );
2664 }
2665 
2666 static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2667 {
2668  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2669 
2670  if ( geom.isNull() )
2671  return QVariant();
2672 
2673  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2674 
2675  QgsGeometry simplified = geom.simplify( tolerance );
2676  if ( simplified.isNull() )
2677  return QVariant();
2678 
2679  return simplified;
2680 }
2681 
2682 static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2683 {
2684  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2685 
2686  if ( geom.isNull() )
2687  return QVariant();
2688 
2689  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2690 
2692 
2693  QgsGeometry simplified = simplifier.simplify( geom );
2694  if ( simplified.isNull() )
2695  return QVariant();
2696 
2697  return simplified;
2698 }
2699 
2700 static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2701 {
2702  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2703 
2704  if ( geom.isNull() )
2705  return QVariant();
2706 
2707  int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
2708  double offset = qBound( 0.0, QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.5 );
2709  double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
2710  double maxAngle = qBound( 0.0, QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 180.0 );
2711 
2712  QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
2713  if ( smoothed.isNull() )
2714  return QVariant();
2715 
2716  return smoothed;
2717 }
2718 
2719 static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2720 {
2721  QVariantList list;
2722  if ( values.size() == 1 && ( values.at( 0 ).type() == QVariant::List || values.at( 0 ).type() == QVariant::StringList ) )
2723  {
2724  list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
2725  }
2726  else
2727  {
2728  list = values;
2729  }
2730 
2731  QVector< QgsGeometry > parts;
2732  parts.reserve( list.size() );
2733  for ( const QVariant &value : qgis::as_const( list ) )
2734  {
2735  if ( value.canConvert<QgsGeometry>() )
2736  {
2737  parts << value.value<QgsGeometry>();
2738  }
2739  else
2740  {
2741  parent->setEvalErrorString( QStringLiteral( "Cannot convert to geometry" ) );
2742  return QgsGeometry();
2743  }
2744  }
2745 
2746  return QgsGeometry::collectGeometry( parts );
2747 }
2748 
2749 static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2750 {
2751  if ( values.count() < 2 || values.count() > 4 )
2752  {
2753  parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
2754  return QVariant();
2755  }
2756 
2757  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2758  double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2759  double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
2760  double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
2761  switch ( values.count() )
2762  {
2763  case 2:
2764  return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
2765  case 3:
2766  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointZ, x, y, z ) ) );
2767  case 4:
2768  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointZM, x, y, z, m ) ) );
2769  }
2770  return QVariant(); //avoid warning
2771 }
2772 
2773 static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2774 {
2775  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2776  double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2777  double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2778  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointM, x, y, 0.0, m ) ) );
2779 }
2780 
2781 static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2782 {
2783  if ( values.empty() )
2784  {
2785  return QVariant();
2786  }
2787 
2788  QVector<QgsPoint> points;
2789  points.reserve( values.count() );
2790 
2791  auto addPoint = [&points]( const QgsGeometry & geom )
2792  {
2793  if ( geom.isNull() )
2794  return;
2795 
2796  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
2797  return;
2798 
2799  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2800  if ( !point )
2801  return;
2802 
2803  points << *point;
2804  };
2805 
2806  for ( const QVariant &value : values )
2807  {
2808  if ( value.type() == QVariant::List )
2809  {
2810  const QVariantList list = value.toList();
2811  for ( const QVariant &v : list )
2812  {
2813  addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
2814  }
2815  }
2816  else
2817  {
2818  addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
2819  }
2820  }
2821 
2822  if ( points.count() < 2 )
2823  return QVariant();
2824 
2825  return QgsGeometry( new QgsLineString( points ) );
2826 }
2827 
2828 static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2829 {
2830  if ( values.count() < 1 )
2831  {
2832  parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
2833  return QVariant();
2834  }
2835 
2836  QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2837  if ( outerRing.type() != QgsWkbTypes::LineGeometry || outerRing.isNull() )
2838  return QVariant();
2839 
2840  std::unique_ptr< QgsPolygon > polygon = qgis::make_unique< QgsPolygon >();
2841 
2842  const QgsCurve *exteriorRing = qgsgeometry_cast< QgsCurve * >( outerRing.constGet() );
2843  if ( !exteriorRing && outerRing.isMultipart() )
2844  {
2845  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
2846  {
2847  if ( collection->numGeometries() == 1 )
2848  {
2849  exteriorRing = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
2850  }
2851  }
2852  }
2853 
2854  if ( !exteriorRing )
2855  return QVariant();
2856 
2857  polygon->setExteriorRing( exteriorRing->segmentize() );
2858 
2859 
2860  for ( int i = 1; i < values.count(); ++i )
2861  {
2862  QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
2863  if ( ringGeom.isNull() )
2864  continue;
2865 
2866  if ( ringGeom.type() != QgsWkbTypes::LineGeometry || ringGeom.isNull() )
2867  continue;
2868 
2869  const QgsCurve *ring = qgsgeometry_cast< QgsCurve * >( ringGeom.constGet() );
2870  if ( !ring && ringGeom.isMultipart() )
2871  {
2872  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
2873  {
2874  if ( collection->numGeometries() == 1 )
2875  {
2876  ring = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
2877  }
2878  }
2879  }
2880 
2881  if ( !ring )
2882  continue;
2883 
2884  polygon->addInteriorRing( ring->segmentize() );
2885  }
2886 
2887  return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
2888 }
2889 
2890 static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2891 {
2892  std::unique_ptr<QgsTriangle> tr( new QgsTriangle() );
2893  std::unique_ptr<QgsLineString> lineString( new QgsLineString() );
2894  lineString->clear();
2895 
2896  for ( const QVariant &value : values )
2897  {
2898  QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
2899  if ( geom.isNull() )
2900  return QVariant();
2901 
2902  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
2903  return QVariant();
2904 
2905  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2906  if ( !point && geom.isMultipart() )
2907  {
2908  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2909  {
2910  if ( collection->numGeometries() == 1 )
2911  {
2912  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
2913  }
2914  }
2915  }
2916 
2917  if ( !point )
2918  return QVariant();
2919 
2920  lineString->addVertex( *point );
2921  }
2922 
2923  tr->setExteriorRing( lineString.release() );
2924 
2925  return QVariant::fromValue( QgsGeometry( tr.release() ) );
2926 }
2927 
2928 static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2929 {
2930  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2931  if ( geom.isNull() )
2932  return QVariant();
2933 
2934  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
2935  return QVariant();
2936 
2937  double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2938  int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2939 
2940  if ( segment < 3 )
2941  {
2942  parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
2943  return QVariant();
2944  }
2945  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2946  if ( !point && geom.isMultipart() )
2947  {
2948  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2949  {
2950  if ( collection->numGeometries() == 1 )
2951  {
2952  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
2953  }
2954  }
2955  }
2956  if ( !point )
2957  return QVariant();
2958 
2959  QgsCircle circ( *point, radius );
2960  return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
2961 }
2962 
2963 static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2964 {
2965  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2966  if ( geom.isNull() )
2967  return QVariant();
2968 
2969  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
2970  return QVariant();
2971 
2972  double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2973  double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2974  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
2975  int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
2976  if ( segment < 3 )
2977  {
2978  parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
2979  return QVariant();
2980  }
2981  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2982  if ( !point && geom.isMultipart() )
2983  {
2984  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2985  {
2986  if ( collection->numGeometries() == 1 )
2987  {
2988  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
2989  }
2990  }
2991  }
2992  if ( !point )
2993  return QVariant();
2994 
2995  QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
2996  return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
2997 }
2998 
2999 static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3000 {
3001 
3002  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3003  if ( pt1.isNull() )
3004  return QVariant();
3005 
3006  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3007  return QVariant();
3008 
3009  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3010  if ( pt2.isNull() )
3011  return QVariant();
3012 
3013  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3014  return QVariant();
3015 
3016  unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
3017  if ( nbEdges < 3 )
3018  {
3019  parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
3020  return QVariant();
3021  }
3022 
3023  QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3025  {
3026  parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
3027  return QVariant();
3028  }
3029 
3030  const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
3031  if ( !center && pt1.isMultipart() )
3032  {
3033  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
3034  {
3035  if ( collection->numGeometries() == 1 )
3036  {
3037  center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3038  }
3039  }
3040  }
3041  if ( !center )
3042  return QVariant();
3043 
3044  const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
3045  if ( !corner && pt2.isMultipart() )
3046  {
3047  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
3048  {
3049  if ( collection->numGeometries() == 1 )
3050  {
3051  corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3052  }
3053  }
3054  }
3055  if ( !corner )
3056  return QVariant();
3057 
3058  QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
3059 
3060  return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
3061 
3062 }
3063 
3064 static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3065 {
3066  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3067  if ( pt1.isNull() )
3068  return QVariant();
3069  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3070  return QVariant();
3071 
3072  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3073  if ( pt2.isNull() )
3074  return QVariant();
3075  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3076  return QVariant();
3077 
3078  const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
3079  const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
3080  QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
3081 
3082  return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
3083 }
3084 
3085 static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3086 {
3087  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3088  if ( pt1.isNull() )
3089  return QVariant();
3090  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3091  return QVariant();
3092 
3093  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3094  if ( pt2.isNull() )
3095  return QVariant();
3096  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3097  return QVariant();
3098 
3099  QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
3100  if ( pt3.isNull() )
3101  return QVariant();
3102  if ( pt3.type() != QgsWkbTypes::PointGeometry || pt3.isMultipart() )
3103  return QVariant();
3104 
3105  QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3106  if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
3107  {
3108  parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
3109  return QVariant();
3110  }
3111  const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
3112  const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
3113  const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
3114  QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
3115  return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
3116 }
3117 
3118 static QVariant pointAt( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) // helper function
3119 {
3120  FEAT_FROM_CONTEXT( context, f )
3121  int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
3122  QgsGeometry g = f.geometry();
3123  if ( g.isNull() )
3124  return QVariant();
3125 
3126  if ( idx < 0 )
3127  {
3128  idx += g.constGet()->nCoordinates();
3129  }
3130  if ( idx < 0 || idx >= g.constGet()->nCoordinates() )
3131  {
3132  parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
3133  return QVariant();
3134  }
3135 
3136  QgsPointXY p = g.vertexAt( idx );
3137  return QVariant( QPointF( p.x(), p.y() ) );
3138 }
3139 
3140 static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )
3141 {
3142  QVariant v = pointAt( values, f, parent );
3143  if ( v.type() == QVariant::PointF )
3144  return QVariant( v.toPointF().x() );
3145  else
3146  return QVariant();
3147 }
3148 static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )
3149 {
3150  QVariant v = pointAt( values, f, parent );
3151  if ( v.type() == QVariant::PointF )
3152  return QVariant( v.toPointF().y() );
3153  else
3154  return QVariant();
3155 }
3156 static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3157 {
3158  FEAT_FROM_CONTEXT( context, f )
3159  QgsGeometry geom = f.geometry();
3160  if ( !geom.isNull() )
3161  return QVariant::fromValue( geom );
3162  else
3163  return QVariant( QVariant::UserType );
3164 }
3165 
3166 static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3167 {
3168  QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3169  QgsGeometry geom = QgsGeometry::fromWkt( wkt );
3170  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3171  return result;
3172 }
3173 
3174 static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3175 {
3176  const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
3177  if ( wkb.isNull() )
3178  return QVariant();
3179 
3180  QgsGeometry geom;
3181  geom.fromWkb( wkb );
3182  return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3183 }
3184 
3185 static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3186 {
3187  QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3188  QgsOgcUtils::Context ogcContext;
3189  if ( context )
3190  {
3191  QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
3192  if ( mapLayerPtr )
3193  {
3194  ogcContext.layer = mapLayerPtr.data();
3195  ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
3196  }
3197  }
3198  QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
3199  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3200  return result;
3201 }
3202 
3203 static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3204 {
3205  FEAT_FROM_CONTEXT( context, f )
3207  QgsDistanceArea *calc = parent->geomCalculator();
3208  if ( calc )
3209  {
3210  double area = calc->measureArea( f.geometry() );
3211  area = calc->convertAreaMeasurement( area, parent->areaUnits() );
3212  return QVariant( area );
3213  }
3214  else
3215  {
3216  return QVariant( f.geometry().area() );
3217  }
3218 }
3219 
3220 static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3221 {
3222  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3223 
3224  if ( geom.type() != QgsWkbTypes::PolygonGeometry )
3225  return QVariant();
3226 
3227  return QVariant( geom.area() );
3228 }
3229 
3230 static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3231 {
3232  FEAT_FROM_CONTEXT( context, f )
3234  QgsDistanceArea *calc = parent->geomCalculator();
3235  if ( calc )
3236  {
3237  double len = calc->measureLength( f.geometry() );
3238  len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
3239  return QVariant( len );
3240  }
3241  else
3242  {
3243  return QVariant( f.geometry().length() );
3244  }
3245 }
3246 
3247 static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3248 {
3249  FEAT_FROM_CONTEXT( context, f )
3251  QgsDistanceArea *calc = parent->geomCalculator();
3252  if ( calc )
3253  {
3254  double len = calc->measurePerimeter( f.geometry() );
3255  len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
3256  return QVariant( len );
3257  }
3258  else
3259  {
3260  return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
3261  }
3262 }
3263 
3264 static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3265 {
3266  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3267 
3268  if ( geom.type() != QgsWkbTypes::PolygonGeometry )
3269  return QVariant();
3270 
3271  //length for polygons = perimeter
3272  return QVariant( geom.length() );
3273 }
3274 
3275 static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3276 {
3277  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3278  return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
3279 }
3280 
3281 static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3282 {
3283  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3284  if ( geom.isNull() )
3285  return QVariant();
3286 
3287  return QVariant( geom.constGet()->partCount() );
3288 }
3289 
3290 static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3291 {
3292  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3293  if ( geom.isNull() )
3294  return QVariant();
3295 
3296  return QVariant( geom.isMultipart() );
3297 }
3298 
3299 static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3300 {
3301  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3302 
3303  if ( geom.isNull() )
3304  return QVariant();
3305 
3306  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3307  if ( curvePolygon )
3308  return QVariant( curvePolygon->numInteriorRings() );
3309 
3310  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3311  if ( collection )
3312  {
3313  //find first CurvePolygon in collection
3314  for ( int i = 0; i < collection->numGeometries(); ++i )
3315  {
3316  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
3317  if ( !curvePolygon )
3318  continue;
3319 
3320  return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
3321  }
3322  }
3323 
3324  return QVariant();
3325 }
3326 
3327 static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3328 {
3329  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3330 
3331  if ( geom.isNull() )
3332  return QVariant();
3333 
3334  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3335  if ( curvePolygon )
3336  return QVariant( curvePolygon->ringCount() );
3337 
3338  bool foundPoly = false;
3339  int ringCount = 0;
3340  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3341  if ( collection )
3342  {
3343  //find CurvePolygons in collection
3344  for ( int i = 0; i < collection->numGeometries(); ++i )
3345  {
3346  curvePolygon = qgsgeometry_cast< QgsCurvePolygon *>( collection->geometryN( i ) );
3347  if ( !curvePolygon )
3348  continue;
3349 
3350  foundPoly = true;
3351  ringCount += curvePolygon->ringCount();
3352  }
3353  }
3354 
3355  if ( !foundPoly )
3356  return QVariant();
3357 
3358  return QVariant( ringCount );
3359 }
3360 
3361 static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3362 {
3363  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3364  QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
3365  QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
3366  return result;
3367 }
3368 
3369 static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3370 {
3371  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3372  return QVariant::fromValue( geom.boundingBox().width() );
3373 }
3374 
3375 static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3376 {
3377  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3378  return QVariant::fromValue( geom.boundingBox().height() );
3379 }
3380 
3381 static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3382 {
3383  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3384  return QVariant::fromValue( geom.boundingBox().xMinimum() );
3385 }
3386 
3387 static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3388 {
3389  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3390  return QVariant::fromValue( geom.boundingBox().xMaximum() );
3391 }
3392 
3393 static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3394 {
3395  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3396  return QVariant::fromValue( geom.boundingBox().yMinimum() );
3397 }
3398 
3399 static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3400 {
3401  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3402  return QVariant::fromValue( geom.boundingBox().yMaximum() );
3403 }
3404 
3405 static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3406 {
3407  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3408 
3409  if ( geom.isNull() || geom.isEmpty( ) )
3410  return QVariant();
3411 
3412  if ( !geom.constGet()->is3D() )
3413  return QVariant();
3414 
3415  double max = std::numeric_limits< double >::lowest();
3416 
3417  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3418  {
3419  double z = ( *it ).z();
3420 
3421  if ( max < z )
3422  max = z;
3423  }
3424 
3425  if ( max == std::numeric_limits< double >::lowest() )
3426  return QVariant( QVariant::Double );
3427 
3428  return QVariant( max );
3429 }
3430 
3431 static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3432 {
3433  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3434 
3435  if ( geom.isNull() || geom.isEmpty() )
3436  return QVariant();
3437 
3438  if ( !geom.constGet()->is3D() )
3439  return QVariant();
3440 
3441  double min = std::numeric_limits< double >::max();
3442 
3443  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3444  {
3445  double z = ( *it ).z();
3446 
3447  if ( z < min )
3448  min = z;
3449  }
3450 
3451  if ( min == std::numeric_limits< double >::max() )
3452  return QVariant( QVariant::Double );
3453 
3454  return QVariant( min );
3455 }
3456 
3457 static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3458 {
3459  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3460 
3461  if ( geom.isNull() || geom.isEmpty() )
3462  return QVariant();
3463 
3464  if ( !geom.constGet()->isMeasure() )
3465  return QVariant();
3466 
3467  double min = std::numeric_limits< double >::max();
3468 
3469  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3470  {
3471  double m = ( *it ).m();
3472 
3473  if ( m < min )
3474  min = m;
3475  }
3476 
3477  if ( min == std::numeric_limits< double >::max() )
3478  return QVariant( QVariant::Double );
3479 
3480  return QVariant( min );
3481 }
3482 
3483 static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3484 {
3485  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3486 
3487  if ( geom.isNull() || geom.isEmpty() )
3488  return QVariant();
3489 
3490  if ( !geom.constGet()->isMeasure() )
3491  return QVariant();
3492 
3493  double max = std::numeric_limits< double >::lowest();
3494 
3495  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3496  {
3497  double m = ( *it ).m();
3498 
3499  if ( max < m )
3500  max = m;
3501  }
3502 
3503  if ( max == std::numeric_limits< double >::lowest() )
3504  return QVariant( QVariant::Double );
3505 
3506  return QVariant( max );
3507 }
3508 
3509 static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3510 {
3511  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3512  if ( geom.isNull() )
3513  return QVariant();
3514 
3515  std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
3516  flipped->swapXy();
3517  return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
3518 }
3519 
3520 static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3521 {
3522  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3523  if ( fGeom.isNull() )
3524  return QVariant();
3525 
3526  const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
3527  if ( !curve && fGeom.isMultipart() )
3528  {
3529  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
3530  {
3531  if ( collection->numGeometries() == 1 )
3532  {
3533  curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
3534  }
3535  }
3536  }
3537 
3538  if ( !curve )
3539  return QVariant();
3540 
3541  return QVariant::fromValue( curve->isClosed() );
3542 }
3543 
3544 static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3545 {
3546  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3547 
3548  if ( geom.isNull() )
3549  return QVariant();
3550 
3551  QVariant result;
3552  if ( !geom.isMultipart() )
3553  {
3554  const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
3555 
3556  if ( !line )
3557  return QVariant();
3558 
3559  std::unique_ptr< QgsLineString > closedLine( line->clone() );
3560  closedLine->close();
3561 
3562  result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
3563  }
3564  else
3565  {
3566  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
3567 
3568  std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
3569 
3570  for ( int i = 0; i < collection->numGeometries(); ++i )
3571  {
3572  if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
3573  {
3574  std::unique_ptr< QgsLineString > closedLine( line->clone() );
3575  closedLine->close();
3576 
3577  closed->addGeometry( closedLine.release() );
3578  }
3579  }
3580  result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
3581  }
3582 
3583  return result;
3584 }
3585 
3586 static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3587 {
3588  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3589  if ( fGeom.isNull() )
3590  return QVariant();
3591 
3592  return QVariant::fromValue( fGeom.isEmpty() );
3593 }
3594 
3595 static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3596 {
3597  if ( values.at( 0 ).isNull() )
3598  return QVariant::fromValue( true );
3599 
3600  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3601  return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
3602 }
3603 
3604 static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3605 {
3606  if ( values.length() < 2 || values.length() > 3 )
3607  return QVariant();
3608 
3609  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3610  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3611 
3612  if ( fGeom.isNull() || sGeom.isNull() )
3613  return QVariant();
3614 
3615  std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
3616 
3617  if ( values.length() == 2 )
3618  {
3619  //two geometry arguments, return relation
3620  QString result = engine->relate( sGeom.constGet() );
3621  return QVariant::fromValue( result );
3622  }
3623  else
3624  {
3625  //three arguments, test pattern
3626  QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3627  bool result = engine->relatePattern( sGeom.constGet(), pattern );
3628  return QVariant::fromValue( result );
3629  }
3630 }
3631 
3632 static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3633 {
3634  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3635  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3636  return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
3637 }
3638 static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3639 {
3640  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3641  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3642  return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
3643 }
3644 static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3645 {
3646  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3647  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3648  return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
3649 }
3650 static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3651 {
3652  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3653  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3654  return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
3655 }
3656 static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3657 {
3658  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3659  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3660  return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
3661 }
3662 static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3663 {
3664  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3665  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3666  return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
3667 }
3668 static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3669 {
3670  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3671  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3672  return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
3673 }
3674 static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3675 {
3676  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3677  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3678  return fGeom.within( sGeom ) ? TVL_True : TVL_False;
3679 }
3680 static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3681 {
3682  if ( values.length() < 2 || values.length() > 3 )
3683  return QVariant();
3684 
3685  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3686  double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3687  int seg = 8;
3688  if ( values.length() == 3 )
3689  seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3690 
3691  QgsGeometry geom = fGeom.buffer( dist, seg );
3692  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3693  return result;
3694 }
3695 
3696 static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3697 {
3698  const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3699  const QgsGeometry reoriented = fGeom.forceRHR();
3700  return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
3701 }
3702 
3703 static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3704 {
3705  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3706  const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
3707  if ( !pt && fGeom.isMultipart() )
3708  {
3709  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
3710  {
3711  if ( collection->numGeometries() == 1 )
3712  {
3713  pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3714  }
3715  }
3716  }
3717 
3718  if ( !pt )
3719  {
3720  parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
3721  return QVariant();
3722  }
3723 
3724  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3725  double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3726  double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3727  double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3728 
3729  QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
3730  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3731  return result;
3732 }
3733 
3734 static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3735 {
3736  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3737  if ( fGeom.type() != QgsWkbTypes::LineGeometry )
3738  {
3739  parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
3740  return QVariant();
3741  }
3742 
3743  double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3744  double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3745  int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3746 
3747  QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
3748  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3749  return result;
3750 }
3751 
3752 static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3753 {
3754  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3755  if ( fGeom.type() != QgsWkbTypes::LineGeometry )
3756  {
3757  parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
3758  return QVariant();
3759  }
3760 
3761  int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
3762 
3763  QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
3764  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3765  return result;
3766 }
3767 
3768 static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3769 {
3770  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3771  double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3772  int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3773  QgsGeometry::JoinStyle join = static_cast< QgsGeometry::JoinStyle >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3774  if ( join < QgsGeometry::JoinStyleRound || join > QgsGeometry::JoinStyleBevel )
3775  return QVariant();
3776  double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3777 
3778  QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
3779  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3780  return result;
3781 }
3782 
3783 static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3784 {
3785  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3786  double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3787  int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3788  QgsGeometry::JoinStyle join = static_cast< QgsGeometry::JoinStyle >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3789  if ( join < QgsGeometry::JoinStyleRound || join > QgsGeometry::JoinStyleBevel )
3790  return QVariant();
3791  double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3792 
3793  QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, QgsGeometry::SideLeft, join, miterLimit );
3794  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3795  return result;
3796 }
3797 
3798 static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3799 {
3800  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3801  double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3802  double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3803 
3804  QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
3805  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3806  return result;
3807 }
3808 
3809 static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3810 {
3811  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3812  double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3813  double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3814  fGeom.translate( dx, dy );
3815  return QVariant::fromValue( fGeom );
3816 }
3817 
3818 static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3819 {
3820  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3821  const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3822  const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
3823  : QgsGeometry();
3824 
3825  QgsPointXY pt;
3826  if ( center.isNull() )
3827  {
3828  // if center wasn't specified, use bounding box centroid
3829  pt = fGeom.boundingBox().center();
3830  }
3831  else if ( center.type() != QgsWkbTypes::PointGeometry )
3832  {
3833  parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
3834  return QVariant();
3835  }
3836  else if ( center.isMultipart() )
3837  {
3838  QgsMultiPointXY multiPoint = center.asMultiPoint();
3839  if ( multiPoint.count() == 1 )
3840  {
3841  pt = multiPoint[0];
3842  }
3843  else
3844  {
3845  parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
3846  return QVariant();
3847  }
3848  }
3849  else
3850  {
3851  pt = center.asPoint();
3852  }
3853 
3854  fGeom.rotate( rotation, pt );
3855  return QVariant::fromValue( fGeom );
3856 }
3857 
3858 static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3859 {
3860  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3861  QgsGeometry geom = fGeom.centroid();
3862  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3863  return result;
3864 }
3865 static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3866 {
3867  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3868  QgsGeometry geom = fGeom.pointOnSurface();
3869  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3870  return result;
3871 }
3872 
3873 static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3874 {
3875  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3876  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3877  QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
3878  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3879  return result;
3880 }
3881 
3882 static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3883 {
3884  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3885  QgsGeometry geom = fGeom.convexHull();
3886  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3887  return result;
3888 }
3889 
3890 
3891 static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3892 {
3893  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3894  int segments = 36;
3895  if ( values.length() == 2 )
3896  segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3897  if ( segments < 0 )
3898  {
3899  parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
3900  return QVariant();
3901  }
3902 
3903  QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
3904  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3905  return result;
3906 }
3907 
3908 static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3909 {
3910  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3911  QgsGeometry geom = fGeom.orientedMinimumBoundingBox( );
3912  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3913  return result;
3914 }
3915 
3916 static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3917 {
3918  const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3919 
3920  // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
3921  // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
3922  // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
3923 
3924  double area, angle, width, height;
3925  const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
3926 
3927  if ( geom.isNull() )
3928  {
3929  parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
3930  return QVariant();
3931  }
3932  return angle;
3933 }
3934 
3935 static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3936 {
3937  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3938  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3939  QgsGeometry geom = fGeom.difference( sGeom );
3940  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3941  return result;
3942 }
3943 
3944 static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3945 {
3946  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3947  if ( fGeom.isNull() )
3948  return QVariant();
3949 
3950  QVariant result;
3951  if ( !fGeom.isMultipart() )
3952  {
3953  const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
3954  if ( !curve )
3955  return QVariant();
3956 
3957  QgsCurve *reversed = curve->reversed();
3958  result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
3959  }
3960  else
3961  {
3962  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
3963  std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
3964  for ( int i = 0; i < collection->numGeometries(); ++i )
3965  {
3966  if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
3967  {
3968  reversed->addGeometry( curve->reversed() );
3969  }
3970  else
3971  {
3972  reversed->addGeometry( collection->geometryN( i )->clone() );
3973  }
3974  }
3975  result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
3976  }
3977  return result;
3978 }
3979 
3980 static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3981 {
3982  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3983  if ( fGeom.isNull() )
3984  return QVariant();
3985 
3986  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
3987  if ( !curvePolygon && fGeom.isMultipart() )
3988  {
3989  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
3990  {
3991  if ( collection->numGeometries() == 1 )
3992  {
3993  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3994  }
3995  }
3996  }
3997 
3998  if ( !curvePolygon || !curvePolygon->exteriorRing() )
3999  return QVariant();
4000 
4001  QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
4002  QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
4003  return result;
4004 }
4005 
4006 static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4007 {
4008  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4009  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4010  return QVariant( fGeom.distance( sGeom ) );
4011 }
4012 
4013 static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4014 {
4015  QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4016  QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4017 
4018  double res = -1;
4019  if ( values.length() == 3 && values.at( 2 ).isValid() )
4020  {
4021  double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4022  densify = qBound( 0.0, densify, 1.0 );
4023  res = g1.hausdorffDistanceDensify( g2, densify );
4024  }
4025  else
4026  {
4027  res = g1.hausdorffDistance( g2 );
4028  }
4029 
4030  return res > -1 ? QVariant( res ) : QVariant();
4031 }
4032 
4033 static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4034 {
4035  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4036  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4037  QgsGeometry geom = fGeom.intersection( sGeom );
4038  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4039  return result;
4040 }
4041 static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4042 {
4043  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4044  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4045  QgsGeometry geom = fGeom.symDifference( sGeom );
4046  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4047  return result;
4048 }
4049 static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4050 {
4051  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4052  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4053  QgsGeometry geom = fGeom.combine( sGeom );
4054  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4055  return result;
4056 }
4057 
4058 static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4059 {
4060  if ( values.length() < 1 || values.length() > 2 )
4061  return QVariant();
4062 
4063  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4064  int prec = 8;
4065  if ( values.length() == 2 )
4066  prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4067  QString wkt = fGeom.asWkt( prec );
4068  return QVariant( wkt );
4069 }
4070 
4071 static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4072 {
4073  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4074  return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
4075 }
4076 
4077 static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4078 {
4079  if ( values.length() != 2 )
4080  {
4081  parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %1 given." ).arg( values.length() ) );
4082  return QVariant();
4083  }
4084 
4085  QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4086  QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4087 
4088  const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
4089  if ( !pt1 && fGeom1.isMultipart() )
4090  {
4091  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
4092  {
4093  if ( collection->numGeometries() == 1 )
4094  {
4095  pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4096  }
4097  }
4098  }
4099 
4100  const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
4101  if ( !pt2 && fGeom2.isMultipart() )
4102  {
4103  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
4104  {
4105  if ( collection->numGeometries() == 1 )
4106  {
4107  pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4108  }
4109  }
4110  }
4111 
4112  if ( !pt1 || !pt2 )
4113  {
4114  parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
4115  return QVariant();
4116  }
4117 
4118  // Code from PostGIS
4119  if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
4120  {
4121  if ( pt1->y() < pt2->y() )
4122  return 0.0;
4123  else if ( pt1->y() > pt2->y() )
4124  return M_PI;
4125  else
4126  return 0;
4127  }
4128 
4129  if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
4130  {
4131  if ( pt1->x() < pt2->x() )
4132  return M_PI_2;
4133  else if ( pt1->x() > pt2->x() )
4134  return M_PI + ( M_PI_2 );
4135  else
4136  return 0;
4137  }
4138 
4139  if ( pt1->x() < pt2->x() )
4140  {
4141  if ( pt1->y() < pt2->y() )
4142  {
4143  return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
4144  }
4145  else /* ( pt1->y() > pt2->y() ) - equality case handled above */
4146  {
4147  return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
4148  + ( M_PI_2 );
4149  }
4150  }
4151 
4152  else /* ( pt1->x() > pt2->x() ) - equality case handled above */
4153  {
4154  if ( pt1->y() > pt2->y() )
4155  {
4156  return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
4157  + M_PI;
4158  }
4159  else /* ( pt1->y() < pt2->y() ) - equality case handled above */
4160  {
4161  return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
4162  + ( M_PI + ( M_PI_2 ) );
4163  }
4164  }
4165 }
4166 
4167 static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4168 {
4169  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4170 
4171  if ( geom.type() != QgsWkbTypes::PointGeometry )
4172  {
4173  parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
4174  return QVariant();
4175  }
4176 
4177  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4178  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4179  double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4180 
4181  const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet() );
4182  QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
4183 
4184  return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
4185 }
4186 
4187 static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4188 {
4189  QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4190  QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4191 
4192  const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
4193  if ( !pt1 && fGeom1.isMultipart() )
4194  {
4195  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
4196  {
4197  if ( collection->numGeometries() == 1 )
4198  {
4199  pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4200  }
4201  }
4202  }
4203  const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
4204  if ( !pt2 && fGeom2.isMultipart() )
4205  {
4206  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
4207  {
4208  if ( collection->numGeometries() == 1 )
4209  {
4210  pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4211  }
4212  }
4213  }
4214 
4215  if ( ( fGeom1.type() != QgsWkbTypes::PointGeometry ) || ( fGeom2.type() != QgsWkbTypes::PointGeometry ) ||
4216  !pt1 || !pt2 )
4217  {
4218  parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
4219  return QVariant();
4220  }
4221 
4222  return pt1->inclination( *pt2 );
4223 
4224 }
4225 
4226 static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4227 {
4228  if ( values.length() != 3 )
4229  return QVariant();
4230 
4231  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4232  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4233  double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4234 
4235  QgsGeometry geom = fGeom.extrude( x, y );
4236 
4237  QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
4238  return result;
4239 }
4240 
4241 static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
4242 {
4243  if ( values.length() < 2 )
4244  return QVariant();
4245 
4246  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4247 
4248  if ( !fGeom.isMultipart() )
4249  return values.at( 0 );
4250 
4251  QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4252  QVariant cachedExpression;
4253  if ( ctx )
4254  cachedExpression = ctx->cachedValue( expString );
4255  QgsExpression expression;
4256 
4257  if ( cachedExpression.isValid() )
4258  {
4259  expression = cachedExpression.value<QgsExpression>();
4260  }
4261  else
4262  expression = QgsExpression( expString );
4263 
4264  bool asc = values.value( 2 ).toBool();
4265 
4266  QgsExpressionContext *unconstedContext = nullptr;
4267  QgsFeature f;
4268  if ( ctx )
4269  {
4270  // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
4271  // so no reason to worry
4272  unconstedContext = const_cast<QgsExpressionContext *>( ctx );
4273  f = ctx->feature();
4274  }
4275  else
4276  {
4277  // If there's no context provided, create a fake one
4278  unconstedContext = new QgsExpressionContext();
4279  }
4280 
4281  const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
4282  Q_ASSERT( collection ); // Should have failed the multipart check above
4283 
4285  orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
4286  QgsExpressionSorter sorter( orderBy );
4287 
4288  QList<QgsFeature> partFeatures;
4289  partFeatures.reserve( collection->partCount() );
4290  for ( int i = 0; i < collection->partCount(); ++i )
4291  {
4292  f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
4293  partFeatures << f;
4294  }
4295 
4296  sorter.sortFeatures( partFeatures, unconstedContext );
4297 
4298  QgsGeometryCollection *orderedGeom = qgsgeometry_cast<QgsGeometryCollection *>( fGeom.constGet()->clone() );
4299 
4300  Q_ASSERT( orderedGeom );
4301 
4302  while ( orderedGeom->partCount() )
4303  orderedGeom->removeGeometry( 0 );
4304 
4305  for ( const QgsFeature &feature : qgis::as_const( partFeatures ) )
4306  {
4307  orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
4308  }
4309 
4310  QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
4311 
4312  if ( !ctx )
4313  delete unconstedContext;
4314 
4315  return result;
4316 }
4317 
4318 static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4319 {
4320  QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4321  QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4322 
4323  QgsGeometry geom = fromGeom.nearestPoint( toGeom );
4324 
4325  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4326  return result;
4327 }
4328 
4329 static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4330 {
4331  QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4332  QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4333 
4334  QgsGeometry geom = fromGeom.shortestLine( toGeom );
4335 
4336  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4337  return result;
4338 }
4339 
4340 static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4341 {
4342  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4343  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4344 
4345  QgsGeometry geom = lineGeom.interpolate( distance );
4346 
4347  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4348  return result;
4349 }
4350 
4351 static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4352 {
4353  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4354  if ( lineGeom.type() != QgsWkbTypes::LineGeometry )
4355  {
4356  parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
4357  return QVariant();
4358  }
4359 
4360  const QgsCurve *curve = nullptr;
4361  if ( !lineGeom.isMultipart() )
4362  curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
4363  else
4364  {
4365  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
4366  {
4367  if ( collection->numGeometries() > 0 )
4368  {
4369  curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4370  }
4371  }
4372  }
4373  if ( !curve )
4374  return QVariant();
4375 
4376  double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4377  double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4378 
4379  std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
4380  QgsGeometry result( std::move( substring ) );
4381  return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
4382 }
4383 
4384 static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4385 {
4386  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4387  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4388 
4389  return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
4390 }
4391 
4392 static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4393 {
4394  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4395  int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4396  if ( vertex < 0 )
4397  {
4398  //negative idx
4399  int count = geom.constGet()->nCoordinates();
4400  vertex = count + vertex;
4401  }
4402 
4403  return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
4404 }
4405 
4406 static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4407 {
4408  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4409  int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4410  if ( vertex < 0 )
4411  {
4412  //negative idx
4413  int count = geom.constGet()->nCoordinates();
4414  vertex = count + vertex;
4415  }
4416 
4417  return geom.distanceToVertex( vertex );
4418 }
4419 
4420 static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4421 {
4422  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4423  QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4424 
4425  double distance = lineGeom.lineLocatePoint( pointGeom );
4426 
4427  return distance >= 0 ? distance : QVariant();
4428 }
4429 
4430 static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4431 {
4432  if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
4433  {
4434  double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4435  return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
4436  }
4437 
4438  if ( values.length() >= 1 )
4439  {
4440  double number = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
4441  return QVariant( qlonglong( std::round( number ) ) );
4442  }
4443 
4444  return QVariant();
4445 }
4446 
4447 static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4448 {
4449  Q_UNUSED( values )
4450  Q_UNUSED( parent )
4451  return M_PI;
4452 }
4453 
4454 static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4455 {
4456  const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4457  const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4458  const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4459  if ( places < 0 )
4460  {
4461  parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
4462  return QVariant();
4463  }
4464 
4465  QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
4466  locale.setNumberOptions( locale.numberOptions() &= ~QLocale::NumberOption::OmitGroupSeparator );
4467  return locale.toString( value, 'f', places );
4468 }
4469 
4470 static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4471 {
4472  const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
4473  const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4474  const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4475 
4476  QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
4477  return locale.toString( datetime, format );
4478 }
4479 
4480 static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
4481 {
4482  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4483  int avg = ( color.red() + color.green() + color.blue() ) / 3;
4484  int alpha = color.alpha();
4485 
4486  color.setRgb( avg, avg, avg, alpha );
4487 
4488  return QgsSymbolLayerUtils::encodeColor( color );
4489 }
4490 
4491 static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4492 {
4493  QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4494  QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
4495  double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4496  if ( ratio > 1 )
4497  {
4498  ratio = 1;
4499  }
4500  else if ( ratio < 0 )
4501  {
4502  ratio = 0;
4503  }
4504 
4505  int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
4506  int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
4507  int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
4508  int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
4509 
4510  QColor newColor( red, green, blue, alpha );
4511 
4512  return QgsSymbolLayerUtils::encodeColor( newColor );
4513 }
4514 
4515 static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4516 {
4517  int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4518  int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4519  int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4520  QColor color = QColor( red, green, blue );
4521  if ( ! color.isValid() )
4522  {
4523  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
4524  color = QColor( 0, 0, 0 );
4525  }
4526 
4527  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
4528 }
4529 
4530 static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4531 {
4532  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
4533  QVariant value = node->eval( parent, context );
4534  if ( parent->hasEvalError() )
4535  {
4536  parent->setEvalErrorString( QString() );
4537  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
4539  value = node->eval( parent, context );
4541  }
4542  return value;
4543 }
4544 
4545 static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4546 {
4547  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
4549  QVariant value = node->eval( parent, context );
4551  if ( value.toBool() )
4552  {
4553  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
4555  value = node->eval( parent, context );
4557  }
4558  else
4559  {
4560  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
4562  value = node->eval( parent, context );
4564  }
4565  return value;
4566 }
4567 
4568 static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4569 {
4570  int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4571  int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4572  int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4573  int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
4574  QColor color = QColor( red, green, blue, alpha );
4575  if ( ! color.isValid() )
4576  {
4577  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
4578  color = QColor( 0, 0, 0 );
4579  }
4580  return QgsSymbolLayerUtils::encodeColor( color );
4581 }
4582 
4583 QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4584 {
4585  QgsGradientColorRamp expRamp;
4586  const QgsColorRamp *ramp = nullptr;
4587  if ( values.at( 0 ).canConvert<QgsGradientColorRamp>() )
4588  {
4589  expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
4590  ramp = &expRamp;
4591  }
4592  else
4593  {
4594  QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4595  ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
4596  if ( ! ramp )
4597  {
4598  parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
4599  return QVariant();
4600  }
4601  }
4602 
4603  double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4604  QColor color = ramp->color( value );
4605  return QgsSymbolLayerUtils::encodeColor( color );
4606 }
4607 
4608 static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4609 {
4610  // Hue ranges from 0 - 360
4611  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
4612  // Saturation ranges from 0 - 100
4613  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4614  // Lightness ranges from 0 - 100
4615  double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4616 
4617  QColor color = QColor::fromHslF( hue, saturation, lightness );
4618 
4619  if ( ! color.isValid() )
4620  {
4621  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
4622  color = QColor( 0, 0, 0 );
4623  }
4624 
4625  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
4626 }
4627 
4628 static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4629 {
4630  // Hue ranges from 0 - 360
4631  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
4632  // Saturation ranges from 0 - 100
4633  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4634  // Lightness ranges from 0 - 100
4635  double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4636  // Alpha ranges from 0 - 255
4637  double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
4638 
4639  QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
4640  if ( ! color.isValid() )
4641  {
4642  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
4643  color = QColor( 0, 0, 0 );
4644  }
4645  return QgsSymbolLayerUtils::encodeColor( color );
4646 }
4647 
4648 static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4649 {
4650  // Hue ranges from 0 - 360
4651  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
4652  // Saturation ranges from 0 - 100
4653  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4654  // Value ranges from 0 - 100
4655  double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4656 
4657  QColor color = QColor::fromHsvF( hue, saturation, value );
4658 
4659  if ( ! color.isValid() )
4660  {
4661  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
4662  color = QColor( 0, 0, 0 );
4663  }
4664 
4665  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
4666 }
4667 
4668 static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4669 {
4670  // Hue ranges from 0 - 360
4671  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
4672  // Saturation ranges from 0 - 100
4673  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4674  // Value ranges from 0 - 100
4675  double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4676  // Alpha ranges from 0 - 255
4677  double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
4678 
4679  QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
4680  if ( ! color.isValid() )
4681  {
4682  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
4683  color = QColor( 0, 0, 0 );
4684  }
4685  return QgsSymbolLayerUtils::encodeColor( color );
4686 }
4687 
4688 static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4689 {
4690  // Cyan ranges from 0 - 100
4691  double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
4692  // Magenta ranges from 0 - 100
4693  double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4694  // Yellow ranges from 0 - 100
4695  double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4696  // Black ranges from 0 - 100
4697  double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
4698 
4699  QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
4700 
4701  if ( ! color.isValid() )
4702  {
4703  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
4704  color = QColor( 0, 0, 0 );
4705  }
4706 
4707  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
4708 }
4709 
4710 static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4711 {
4712  // Cyan ranges from 0 - 100
4713  double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
4714  // Magenta ranges from 0 - 100
4715  double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
4716  // Yellow ranges from 0 - 100
4717  double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
4718  // Black ranges from 0 - 100
4719  double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
4720  // Alpha ranges from 0 - 255
4721  double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
4722 
4723  QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
4724  if ( ! color.isValid() )
4725  {
4726  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
4727  color = QColor( 0, 0, 0 );
4728  }
4729  return QgsSymbolLayerUtils::encodeColor( color );
4730 }
4731 
4732 static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4733 {
4734  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4735  if ( ! color.isValid() )
4736  {
4737  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
4738  return QVariant();
4739  }
4740 
4741  QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4742  if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
4743  return color.red();
4744  else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
4745  return color.green();
4746  else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
4747  return color.blue();
4748  else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
4749  return color.alpha();
4750  else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
4751  return color.hsvHueF() * 360;
4752  else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
4753  return color.hsvSaturationF() * 100;
4754  else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
4755  return color.valueF() * 100;
4756  else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
4757  return color.hslHueF() * 360;
4758  else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
4759  return color.hslSaturationF() * 100;
4760  else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
4761  return color.lightnessF() * 100;
4762  else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
4763  return color.cyanF() * 100;
4764  else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
4765  return color.magentaF() * 100;
4766  else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
4767  return color.yellowF() * 100;
4768  else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
4769  return color.blackF() * 100;
4770 
4771  parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
4772  return QVariant();
4773 }
4774 
4775 static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4776 {
4777  const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
4778  if ( map.count() < 1 )
4779  {
4780  parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
4781  return QVariant();
4782  }
4783 
4784  QList< QColor > colors;
4785  QgsGradientStopsList stops;
4786  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
4787  {
4788  colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
4789  if ( !colors.last().isValid() )
4790  {
4791  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
4792  return QVariant();
4793  }
4794 
4795  double step = it.key().toDouble();
4796  if ( it == map.constBegin() )
4797  {
4798  if ( step != 0.0 )
4799  stops << QgsGradientStop( step, colors.last() );
4800  }
4801  else if ( it == map.constEnd() )
4802  {
4803  if ( step != 1.0 )
4804  stops << QgsGradientStop( step, colors.last() );
4805  }
4806  else
4807  {
4808  stops << QgsGradientStop( step, colors.last() );
4809  }
4810  }
4811  bool discrete = values.at( 1 ).toBool();
4812 
4813  return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
4814 }
4815 
4816 static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4817 {
4818  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4819  if ( ! color.isValid() )
4820  {
4821  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
4822  return QVariant();
4823  }
4824 
4825  QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4826  int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4827  if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
4828  color.setRed( value );
4829  else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
4830  color.setGreen( value );
4831  else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
4832  color.setBlue( value );
4833  else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
4834  color.setAlpha( value );
4835  else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
4836  color.setHsv( value, color.hsvSaturation(), color.value(), color.alpha() );
4837  else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
4838  color.setHsvF( color.hsvHueF(), value / 100.0, color.valueF(), color.alphaF() );
4839  else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
4840  color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), value / 100.0, color.alphaF() );
4841  else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
4842  color.setHsl( value, color.hslSaturation(), color.lightness(), color.alpha() );
4843  else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
4844  color.setHslF( color.hslHueF(), value / 100.0, color.lightnessF(), color.alphaF() );
4845  else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
4846  color.setHslF( color.hslHueF(), color.hslSaturationF(), value / 100.0, color.alphaF() );
4847  else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
4848  color.setCmykF( value / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
4849  else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
4850  color.setCmykF( color.cyanF(), value / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
4851  else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
4852  color.setCmykF( color.cyanF(), color.magentaF(), value / 100.0, color.blackF(), color.alphaF() );
4853  else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
4854  color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), value / 100.0, color.alphaF() );
4855  else
4856  {
4857  parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
4858  return QVariant();
4859  }
4860  return QgsSymbolLayerUtils::encodeColor( color );
4861 }
4862 
4863 static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4864 {
4865  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4866  if ( ! color.isValid() )
4867  {
4868  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
4869  return QVariant();
4870  }
4871 
4872  color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
4873 
4874  return QgsSymbolLayerUtils::encodeColor( color );
4875 }
4876 
4877 static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4878 {
4879  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
4880  if ( ! color.isValid() )
4881  {
4882  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
4883  return QVariant();
4884  }
4885 
4886  color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
4887 
4888  return QgsSymbolLayerUtils::encodeColor( color );
4889 }
4890 
4891 static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4892 {
4893  QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
4894  QgsGeometry geom = feat.geometry();
4895  if ( !geom.isNull() )
4896  return QVariant::fromValue( geom );
4897  return QVariant();
4898 }
4899 
4900 static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4901 {
4902  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4903  QString sAuthId = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4904  QString dAuthId = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4905 
4907  if ( ! s.isValid() )
4908  return QVariant::fromValue( fGeom );
4910  if ( ! d.isValid() )
4911  return QVariant::fromValue( fGeom );
4912 
4914  if ( context )
4915  tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
4916  QgsCoordinateTransform t( s, d, tContext );
4917  try
4918  {
4919  if ( fGeom.transform( t ) == 0 )
4920  return QVariant::fromValue( fGeom );
4921  }
4922  catch ( QgsCsException &cse )
4923  {
4924  QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
4925  return QVariant();
4926  }
4927  return QVariant();
4928 }
4929 
4930 
4931 static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4932 {
4933  QVariant result;
4934  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
4935  if ( vl )
4936  {
4937  QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
4938 
4939  QgsFeatureRequest req;
4940  req.setFilterFid( fid );
4941  req.setTimeout( 10000 );
4942  req.setRequestMayBeNested( true );
4943  QgsFeatureIterator fIt = vl->getFeatures( req );
4944 
4945  QgsFeature fet;
4946  if ( fIt.nextFeature( fet ) )
4947  result = QVariant::fromValue( fet );
4948  }
4949 
4950  return result;
4951 }
4952 
4953 static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4954 {
4955  //arguments: 1. layer id / name, 2. key attribute, 3. eq value
4956 
4957  std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), parent );
4958 
4959  //no layer found
4960  if ( !featureSource )
4961  {
4962  return QVariant();
4963  }
4964 
4965  QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4966  int attributeId = featureSource->fields().lookupField( attribute );
4967  if ( attributeId == -1 )
4968  {
4969  return QVariant();
4970  }
4971 
4972  const QVariant &attVal = values.at( 2 );
4973 
4974  const QString cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
4975  if ( context && context->hasCachedValue( cacheValueKey ) )
4976  {
4977  return context->cachedValue( cacheValueKey );
4978  }
4979 
4980  QgsFeatureRequest req;
4981  req.setFilterExpression( QStringLiteral( "%1=%2" ).arg( QgsExpression::quotedColumnRef( attribute ),
4982  QgsExpression::quotedString( attVal.toString() ) ) );
4983  req.setLimit( 1 );
4984  req.setTimeout( 10000 );
4985  req.setRequestMayBeNested( true );
4986  if ( !parent->needsGeometry() )
4987  {
4989  }
4990  QgsFeatureIterator fIt = featureSource->getFeatures( req );
4991 
4992  QgsFeature fet;
4993  QVariant res;
4994  if ( fIt.nextFeature( fet ) )
4995  {
4996  res = QVariant::fromValue( fet );
4997  }
4998 
4999  if ( context )
5000  context->setCachedValue( cacheValueKey, res );
5001  return res;
5002 }
5003 
5004 static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
5005 {
5006  QVariant result;
5007  QString fieldName;
5008 
5009  if ( context )
5010  {
5011  if ( !values.isEmpty() )
5012  {
5013  QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
5014  if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
5015  fieldName = col->name();
5016  else if ( values.size() == 2 )
5017  fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5018  }
5019 
5020  QVariant value = values.at( 0 );
5021 
5022  const QgsFields fields = context->fields();
5023  int fieldIndex = fields.lookupField( fieldName );
5024 
5025  if ( fieldIndex == -1 )
5026  {
5027  parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
5028  }
5029  else
5030  {
5031  QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
5032 
5033  const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
5034  if ( context->hasCachedValue( cacheValueKey ) )
5035  {
5036  return context->cachedValue( cacheValueKey );
5037  }
5038 
5039  const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
5041 
5042  const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
5043 
5044  QVariant cache;
5045  if ( !context->hasCachedValue( cacheKey ) )
5046  {
5047  cache = formatter->createCache( layer, fieldIndex, setup.config() );
5048  context->setCachedValue( cacheKey, cache );
5049  }
5050  else
5051  cache = context->cachedValue( cacheKey );
5052 
5053  result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
5054 
5055  context->setCachedValue( cacheValueKey, result );
5056  }
5057  }
5058  else
5059  {
5060  parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
5061  }
5062 
5063  return result;
5064 }
5065 
5066 static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5067 {
5068  QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
5069 
5070  if ( !layer )
5071  return QVariant();
5072 
5073  // here, we always prefer the layer metadata values over the older server-specific published values
5074  QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5075  if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
5076  return layer->name();
5077  else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
5078  return layer->id();
5079  else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
5080  return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->title();
5081  else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
5082  return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->abstract();
5083  else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
5084  {
5085  QStringList keywords;
5086  const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
5087  for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
5088  {
5089  keywords.append( it.value() );
5090  }
5091  if ( !keywords.isEmpty() )
5092  return keywords;
5093  return layer->keywordList();
5094  }
5095  else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
5096  return layer->dataUrl();
5097  else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
5098  {
5099  return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->attribution() );
5100  }
5101  else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
5102  return layer->attributionUrl();
5103  else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
5104  return layer->publicSource();
5105  else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
5106  return layer->minimumScale();
5107  else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
5108  return layer->maximumScale();
5109  else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
5110  return layer->isEditable();
5111  else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
5112  return layer->crs().authid();
5113  else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
5114  return layer->crs().toProj();
5115  else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
5116  return layer->crs().description();
5117  else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
5118  {
5119  QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
5120  QVariant result = QVariant::fromValue( extentGeom );
5121  return result;
5122  }
5123  else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
5124  return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
5125  else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
5126  {
5127  switch ( layer->type() )
5128  {
5130  return QCoreApplication::translate( "expressions", "Vector" );
5132  return QCoreApplication::translate( "expressions", "Raster" );
5134  return QCoreApplication::translate( "expressions", "Mesh" );
5136  return QCoreApplication::translate( "expressions", "Vector Tile" );
5138  return QCoreApplication::translate( "expressions", "Plugin" );
5140  return QCoreApplication::translate( "expressions", "Annotation" );
5141  }
5142  }
5143  else
5144  {
5145  //vector layer methods
5146  QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
5147  if ( vLayer )
5148  {
5149  if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
5150  return vLayer->storageType();
5151  else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
5153  else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
5154  return QVariant::fromValue( vLayer->featureCount() );
5155  else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
5156  {
5157  if ( vLayer->dataProvider() )
5158  {
5159  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
5160  return decodedUri.value( QStringLiteral( "path" ) );
5161  }
5162  }
5163  }
5164  }
5165 
5166  return QVariant();
5167 }
5168 
5169 static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5170 {
5171  QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
5172  if ( !layer )
5173  {
5174  parent->setEvalErrorString( QObject::tr( "Cannot find layer %1" ).arg( values.at( 0 ).toString() ) );
5175  return QVariant();
5176  }
5177 
5178  if ( !layer->dataProvider() )
5179  {
5180  parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
5181  return QVariant();
5182  }
5183 
5184  const QString uriPart = values.at( 1 ).toString();
5185 
5186  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
5187 
5188  if ( !uriPart.isNull() )
5189  {
5190  return decodedUri.value( values.at( 1 ).toString() );
5191  }
5192  else
5193  {
5194  return decodedUri;
5195  }
5196 }
5197 
5198 static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5199 {
5200  QString layerIdOrName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5201 
5202  //try to find a matching layer by name
5203  QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerIdOrName ); //search by id first
5204  if ( !layer )
5205  {
5206  QList<QgsMapLayer *> layersByName = QgsProject::instance()->mapLayersByName( layerIdOrName );
5207  if ( !layersByName.isEmpty() )
5208  {
5209  layer = layersByName.at( 0 );
5210  }
5211  }
5212 
5213  if ( !layer )
5214  return QVariant();
5215 
5216  QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
5217  if ( !rl )
5218  return QVariant();
5219 
5220  int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5221  if ( band < 1 || band > rl->bandCount() )
5222  {
5223  parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer %2" ).arg( band ).arg( layerIdOrName ) );
5224  return QVariant();
5225  }
5226 
5227  QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5228  int stat = 0;
5229 
5230  if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
5231  stat = QgsRasterBandStats::Mean;
5232  else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
5234  else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
5235  stat = QgsRasterBandStats::Min;
5236  else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
5237  stat = QgsRasterBandStats::Max;
5238  else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
5240  else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
5241  stat = QgsRasterBandStats::Sum;
5242  else
5243  {
5244  parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
5245  return QVariant();
5246  }
5247 
5248  QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
5249  switch ( stat )
5250  {
5252  return stats.mean;
5254  return stats.stdDev;
5256  return stats.minimumValue;
5258  return stats.maximumValue;
5260  return stats.range;
5262  return stats.sum;
5263  }
5264  return QVariant();
5265 }
5266 
5267 static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5268 {
5269  return values;
5270 }
5271 
5272 static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5273 {
5274  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5275  bool ascending = values.value( 1 ).toBool();
5276  std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
5277  return list;
5278 }
5279 
5280 static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5281 {
5282  return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
5283 }
5284 
5285 static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5286 {
5287  return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
5288 }
5289 
5290 static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5291 {
5292  QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5293  QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
5294  int match = 0;
5295  for ( const auto &item : listB )
5296  {
5297  if ( listA.contains( item ) )
5298  match++;
5299  }
5300 
5301  return QVariant( match == listB.count() );
5302 }
5303 
5304 static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5305 {
5306  return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
5307 }
5308 
5309 static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5310 {
5311  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5312  const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5313  if ( pos < 0 || pos >= list.length() ) return QVariant();
5314  return list.at( pos );
5315 }
5316 
5317 static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5318 {
5319  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5320  return list.value( 0 );
5321 }
5322 
5323 static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5324 {
5325  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5326  return list.value( list.size() - 1 );
5327 }
5328 
5329 static QVariant convertToSameType( const QVariant &value, QVariant::Type type )
5330 {
5331  QVariant result = value;
5332  result.convert( static_cast<int>( type ) );
5333  return result;
5334 }
5335 
5336 static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5337 {
5338  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5339  list.append( values.at( 1 ) );
5340  return convertToSameType( list, values.at( 0 ).type() );
5341 }
5342 
5343 static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5344 {
5345  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5346  list.prepend( values.at( 1 ) );
5347  return convertToSameType( list, values.at( 0 ).type() );
5348 }
5349 
5350 static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5351 {
5352  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5353  list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
5354  return convertToSameType( list, values.at( 0 ).type() );
5355 }
5356 
5357 static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5358 {
5359  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5360  list.removeAt( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5361  return convertToSameType( list, values.at( 0 ).type() );
5362 }
5363 
5364 static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5365 {
5366  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5367  list.removeAll( values.at( 1 ) );
5368  return convertToSameType( list, values.at( 0 ).type() );
5369 }
5370 
5371 static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5372 {
5373  QVariantList list;
5374  for ( const QVariant &cur : values )
5375  {
5376  list += QgsExpressionUtils::getListValue( cur, parent );
5377  }
5378  return convertToSameType( list, values.at( 0 ).type() );
5379 }
5380 
5381 static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5382 {
5383  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5384  int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5385  const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5386  int slice_length = 0;
5387  // negative positions means positions taken relative to the end of the array
5388  if ( start_pos < 0 )
5389  {
5390  start_pos = list.length() + start_pos;
5391  }
5392  if ( end_pos >= 0 )
5393  {
5394  slice_length = end_pos - start_pos + 1;
5395  }
5396  else
5397  {
5398  slice_length = list.length() + end_pos - start_pos + 1;
5399  }
5400  //avoid negative lengths in QList.mid function
5401  if ( slice_length < 0 )
5402  {
5403  slice_length = 0;
5404  }
5405  list = list.mid( start_pos, slice_length );
5406  return list;
5407 }
5408 
5409 static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5410 {
5411  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5412  std::reverse( list.begin(), list.end() );
5413  return list;
5414 }
5415 
5416 static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5417 {
5418  const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5419  const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
5420  for ( const QVariant &cur : array2 )
5421  {
5422  if ( array1.contains( cur ) )
5423  return QVariant( true );
5424  }
5425  return QVariant( false );
5426 }
5427 
5428 static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5429 {
5430  QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5431 
5432  QVariantList distinct;
5433 
5434  for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
5435  {
5436  if ( !distinct.contains( *it ) )
5437  {
5438  distinct += ( *it );
5439  }
5440  }
5441 
5442  return distinct;
5443 }
5444 
5445 static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5446 {
5447  QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5448  QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5449  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5450 
5451  QString str;
5452 
5453  for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
5454  {
5455  str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
5456  if ( it != ( array.constEnd() - 1 ) )
5457  {
5458  str += delimiter;
5459  }
5460  }
5461 
5462  return QVariant( str );
5463 }
5464 
5465 static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5466 {
5467  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5468  QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5469  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5470 
5471  QStringList list = str.split( delimiter );
5472  QVariantList array;
5473 
5474  for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
5475  {
5476  array += ( !( *it ).isEmpty() ) ? *it : empty;
5477  }
5478 
5479  return array;
5480 }
5481 
5482 static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5483 {
5484  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5485  QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
5486  if ( document.isNull() )
5487  return QVariant();
5488 
5489  return document.toVariant();
5490 }
5491 
5492 static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5493 {
5494  Q_UNUSED( parent )
5495  QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
5496  return document.toJson( QJsonDocument::Compact );
5497 }
5498 
5499 static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5500 {
5501  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5502  if ( str.isEmpty() )
5503  return QVariantMap();
5504  str = str.trimmed();
5505 
5506  return QgsHstoreUtils::parse( str );
5507 }
5508 
5509 static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5510 {
5511  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
5512  return QgsHstoreUtils::build( map );
5513 }
5514 
5515 static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5516 {
5517  QVariantMap result;
5518  for ( int i = 0; i + 1 < values.length(); i += 2 )
5519  {
5520  result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
5521  }
5522  return result;
5523 }
5524 
5525 static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5526 {
5527  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
5528 }
5529 
5530 static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5531 {
5532  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
5533 }
5534 
5535 static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5536 {
5537  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
5538  map.remove( values.at( 1 ).toString() );
5539  return map;
5540 }
5541 
5542 static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5543 {
5544  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
5545  map.insert( values.at( 1 ).toString(), values.at( 2 ) );
5546  return map;
5547 }
5548 
5549 static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5550 {
5551  QVariantMap result;
5552  for ( const QVariant &cur : values )
5553  {
5554  const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
5555  for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
5556  result.insert( it.key(), it.value() );
5557  }
5558  return result;
5559 }
5560 
5561 static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5562 {
5563  return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
5564 }
5565 
5566 static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5567 {
5568  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
5569 }
5570 
5571 static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5572 {
5573  QString envVarName = values.at( 0 ).toString();
5574  return QProcessEnvironment::systemEnvironment().value( envVarName );
5575 }
5576 
5577 static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5578 {
5579  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5580  return QFileInfo( file ).completeBaseName();
5581 }
5582 
5583 static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5584 {
5585  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5586  return QFileInfo( file ).completeSuffix();
5587 }
5588 
5589 static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5590 {
5591  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5592  return QFileInfo::exists( file );
5593 }
5594 
5595 static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5596 {
5597  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5598  return QFileInfo( file ).fileName();
5599 }
5600 
5601 static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5602 {
5603  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5604  return QFileInfo( file ).isFile();
5605 }
5606 
5607 static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5608 {
5609  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5610  return QFileInfo( file ).isDir();
5611 }
5612 
5613 static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5614 {
5615  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5616  return QDir::toNativeSeparators( QFileInfo( file ).path() );
5617 }
5618 
5619 static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5620 {
5621  const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5622  return QFileInfo( file ).size();
5623 }
5624 
5625 static QVariant fcnHash( const QString str, const QCryptographicHash::Algorithm algorithm )
5626 {
5627  return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
5628 }
5629 
5630 static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5631 {
5632  QVariant hash;
5633  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5634  QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
5635 
5636  if ( method == QLatin1String( "md4" ) )
5637  {
5638  hash = fcnHash( str, QCryptographicHash::Md4 );
5639  }
5640  else if ( method == QLatin1String( "md5" ) )
5641  {
5642  hash = fcnHash( str, QCryptographicHash::Md5 );
5643  }
5644  else if ( method == QLatin1String( "sha1" ) )
5645  {
5646  hash = fcnHash( str, QCryptographicHash::Sha1 );
5647  }
5648  else if ( method == QLatin1String( "sha224" ) )
5649  {
5650  hash = fcnHash( str, QCryptographicHash::Sha224 );
5651  }
5652  else if ( method == QLatin1String( "sha256" ) )
5653  {
5654  hash = fcnHash( str, QCryptographicHash::Sha256 );
5655  }
5656  else if ( method == QLatin1String( "sha384" ) )
5657  {
5658  hash = fcnHash( str, QCryptographicHash::Sha384 );
5659  }
5660  else if ( method == QLatin1String( "sha512" ) )
5661  {
5662  hash = fcnHash( str, QCryptographicHash::Sha512 );
5663  }
5664  else if ( method == QLatin1String( "sha3_224" ) )
5665  {
5666  hash = fcnHash( str, QCryptographicHash::Sha3_224 );
5667  }
5668  else if ( method == QLatin1String( "sha3_256" ) )
5669  {
5670  hash = fcnHash( str, QCryptographicHash::Sha3_256 );
5671  }
5672  else if ( method == QLatin1String( "sha3_384" ) )
5673  {
5674  hash = fcnHash( str, QCryptographicHash::Sha3_384 );
5675  }
5676  else if ( method == QLatin1String( "sha3_512" ) )
5677  {
5678  hash = fcnHash( str, QCryptographicHash::Sha3_512 );
5679  }
5680 #if QT_VERSION >= QT_VERSION_CHECK( 5, 9, 2 )
5681  else if ( method == QLatin1String( "keccak_224" ) )
5682  {
5683  hash = fcnHash( str, QCryptographicHash::Keccak_224 );
5684  }
5685  else if ( method == QLatin1String( "keccak_256" ) )
5686  {
5687  hash = fcnHash( str, QCryptographicHash::Keccak_256 );
5688  }
5689  else if ( method == QLatin1String( "keccak_384" ) )
5690  {
5691  hash = fcnHash( str, QCryptographicHash::Keccak_384 );
5692  }
5693  else if ( method == QLatin1String( "keccak_512" ) )
5694  {
5695  hash = fcnHash( str, QCryptographicHash::Keccak_512 );
5696  }
5697 #endif
5698  else
5699  {
5700  parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
5701  }
5702  return hash;
5703 }
5704 
5705 static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5706 {
5707  return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
5708 }
5709 
5710 static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5711 {
5712  return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
5713 }
5714 
5715 static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5716 {
5717  const QByteArray input = values.at( 0 ).toByteArray();
5718  return QVariant( QString( input.toBase64() ) );
5719 }
5720 
5721 static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5722 {
5723  const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5724  const QByteArray base64 = value.toLocal8Bit();
5725  const QByteArray decoded = QByteArray::fromBase64( base64 );
5726  return QVariant( decoded );
5727 }
5728 
5729 typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
5730 
5731 static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const RelationFunction &relationFunction, bool invert = false, double bboxGrow = 0, bool isNearestFunc = false )
5732 {
5733 
5734  const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
5735  QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, parent );
5736 
5737  QgsFeatureRequest request;
5738  request.setTimeout( 10000 );
5739  request.setRequestMayBeNested( true );
5740 
5741  // First parameter is the overlay layer
5742  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
5744 
5745  const bool layerCanBeCached = node->isStatic( parent, context );
5746  QVariant targetLayerValue = node->eval( parent, context );
5748 
5749  // Second parameter is the expression to evaluate (or null for testonly)
5750  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
5752  QString subExpString = node->dump();
5753 
5754  bool testOnly = ( subExpString == "NULL" );
5755  QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, parent );
5756  if ( !targetLayer ) // No layer, no joy
5757  {
5758  parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
5759  return QVariant();
5760  }
5761 
5762  // Third parameter is the filtering expression
5763  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
5765  QString filterString = node->dump();
5766  if ( filterString != "NULL" )
5767  {
5768  request.setFilterExpression( filterString ); //filter cached features
5769  }
5770 
5771  // Fourth parameter is the limit
5772  node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
5774  QVariant limitValue = node->eval( parent, context );
5776  qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
5777 
5778  // Fifth parameter (for nearest only) is the max distance
5779  double max_distance = 0;
5780  if ( isNearestFunc ) //maxdistance param handling
5781  {
5782  node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
5784  QVariant distanceValue = node->eval( parent, context );
5786  max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
5787  }
5788 
5789  // Fifth or sixth (for nearest only) parameter is the cache toggle
5790  node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
5792  QVariant cacheValue = node->eval( parent, context );
5794  bool cacheEnabled = cacheValue.toBool();
5795 
5796 
5797  FEAT_FROM_CONTEXT( context, feat )
5798  const QgsGeometry geometry = feat.geometry();
5799 
5800  if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
5801  {
5802  QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5803  request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
5804  }
5805 
5806  bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
5807 
5808  QgsRectangle intDomain = geometry.boundingBox();
5809  if ( bboxGrow != 0 )
5810  {
5811  intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
5812  }
5813 
5814  const QString cacheBase { QStringLiteral( "%1:%2" ).arg( targetLayer->id(), subExpString ) };
5815 
5816  // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
5817  // Otherwise, it can be toggled by the user
5818  QgsSpatialIndex spatialIndex;
5819  QgsVectorLayer *cachedTarget;
5820  QList<QgsFeature> features;
5821  if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
5822  {
5823  // If the cache (local spatial index) is enabled, we materialize the whole
5824  // layer, then do the request on that layer instead.
5825  const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
5826  const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
5827 
5828  if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
5829  {
5830  cachedTarget = targetLayer->materialize( request );
5831  if ( layerCanBeCached )
5832  context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
5833  }
5834  else
5835  {
5836  cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
5837  }
5838 
5839  if ( !context->hasCachedValue( cacheIndex ) )
5840  {
5841  spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
5842  if ( layerCanBeCached )
5843  context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
5844  }
5845  else
5846  {
5847  spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
5848  }
5849 
5850  QList<QgsFeatureId> fidsList;
5851  if ( isNearestFunc )
5852  {
5853  fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
5854  }
5855  else
5856  {
5857  fidsList = spatialIndex.intersects( intDomain );
5858  }
5859 
5860  QListIterator<QgsFeatureId> i( fidsList );
5861  while ( i.hasNext() )
5862  {
5863  QgsFeatureId fId2 = i.next();
5864  if ( sameLayers && feat.id() == fId2 )
5865  continue;
5866  features.append( cachedTarget->getFeature( fId2 ) );
5867  }
5868 
5869  }
5870  else
5871  {
5872  // If the cache (local spatial index) is not enabled, we directly
5873  // get the features from the target layer
5874  request.setFilterRect( intDomain );
5875  QgsFeatureIterator fit = targetLayer->getFeatures( request );
5876  QgsFeature feat2;
5877  while ( fit.nextFeature( feat2 ) )
5878  {
5879  if ( sameLayers && feat.id() == feat2.id() )
5880  continue;
5881  features.append( feat2 );
5882  }
5883  }
5884 
5885  QgsExpression subExpression;
5886  QgsExpressionContext subContext;
5887  if ( !testOnly )
5888  {
5889  const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
5890  const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
5891 
5892  if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
5893  {
5894  subExpression = QgsExpression( subExpString );
5896  subExpression.prepare( &subContext );
5897  }
5898  else
5899  {
5900  subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
5901  subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
5902  }
5903  }
5904 
5905 
5906  bool found = false;
5907  int foundCount = 0;
5908  QVariantList results;
5909 
5910  QListIterator<QgsFeature> i( features );
5911  while ( i.hasNext() && ( limit == -1 || foundCount < limit ) )
5912  {
5913  QgsFeature feat2 = i.next();
5914 
5915  if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
5916  {
5917  found = true;
5918  foundCount++;
5919 
5920  // We just want a single boolean result if there is any intersect: finish and return true
5921  if ( testOnly )
5922  break;
5923 
5924  if ( !invert )
5925  {
5926  // We want a list of attributes / geometries / other expression values, evaluate now
5927  subContext.setFeature( feat2 );
5928  results.append( subExpression.