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