QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsexpressionfunction.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsexpressionfunction.cpp
3  -------------------
4  begin : May 2017
5  copyright : (C) 2017 Matthias Kuhn
6  email : [email protected]
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 
17 #include <random>
18 
19 #include "qgscoordinateformatter.h"
20 #include "qgscoordinateutils.h"
21 #include "qgsexpressionfunction.h"
22 #include "qgsexpressionutils.h"
23 #include "qgsexpressionnodeimpl.h"
24 #include "qgsexiftools.h"
25 #include "qgsfeaturerequest.h"
26 #include "qgsgeos.h"
27 #include "qgsstringutils.h"
28 #include "qgsmultipoint.h"
29 #include "qgsgeometryutils.h"
30 #include "qgshstoreutils.h"
31 #include "qgsmultilinestring.h"
32 #include "qgslinestring.h"
33 #include "qgscurvepolygon.h"
35 #include "qgspolygon.h"
36 #include "qgstriangle.h"
37 #include "qgscurve.h"
38 #include "qgsregularpolygon.h"
39 #include "qgsquadrilateral.h"
40 #include "qgsmultipolygon.h"
41 #include "qgsogcutils.h"
42 #include "qgsdistancearea.h"
43 #include "qgsgeometryengine.h"
44 #include "qgsexpressionsorter_p.h"
45 #include "qgssymbollayerutils.h"
46 #include "qgsstyle.h"
47 #include "qgsexception.h"
48 #include "qgsmessagelog.h"
49 #include "qgsrasterlayer.h"
50 #include "qgsvectorlayer.h"
51 #include "qgsrasterbandstats.h"
52 #include "qgscolorramp.h"
54 #include "qgsfieldformatter.h"
56 #include "qgsproviderregistry.h"
57 #include "sqlite3.h"
58 #include "qgstransaction.h"
59 #include "qgsthreadingutils.h"
60 #include "qgsapplication.h"
61 #include "qgis.h"
63 #include "qgsunittypes.h"
64 #include "qgsspatialindex.h"
65 #include "qgscolorrampimpl.h"
66 
67 #include <QMimeDatabase>
68 #include <QProcessEnvironment>
69 #include <QCryptographicHash>
70 #include <QRegularExpression>
71 #include <QUuid>
72 #include <QUrlQuery>
73 
74 typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
75 
76 Q_GLOBAL_STATIC( ExpressionFunctionList, sOwnedFunctions )
77 Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
79 
82 Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
83 
84 const QString QgsExpressionFunction::helpText() const
85 {
86  return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
87 }
88 
90 {
91  Q_UNUSED( node )
92  // evaluate arguments
93  QVariantList argValues;
94  if ( args )
95  {
96  int arg = 0;
97  const QList< QgsExpressionNode * > argList = args->list();
98  for ( QgsExpressionNode *n : argList )
99  {
100  QVariant v;
101  if ( lazyEval() )
102  {
103  // Pass in the node for the function to eval as it needs.
104  v = QVariant::fromValue( n );
105  }
106  else
107  {
108  v = n->eval( parent, context );
110  bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
111  if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
112  return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
113  }
114  argValues.append( v );
115  arg++;
116  }
117  }
118 
119  return func( argValues, context, parent, node );
120 }
121 
123 {
124  Q_UNUSED( node )
125  return true;
126 }
127 
129 {
130  return QStringList();
131 }
132 
134 {
135  Q_UNUSED( parent )
136  Q_UNUSED( context )
137  Q_UNUSED( node )
138  return false;
139 }
140 
142 {
143  Q_UNUSED( parent )
144  Q_UNUSED( context )
145  Q_UNUSED( node )
146  return true;
147 }
148 
150 {
151  Q_UNUSED( node )
152  return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
153 }
154 
156 {
157  return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
158 }
159 
161 {
162  return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
163 }
164 
166 {
167  return mHandlesNull;
168 }
169 
170 // doxygen doesn't like this constructor for some reason (maybe the function arguments?)
173  FcnEval fcn,
174  const QString &group,
175  const QString &helpText,
176  const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
177  const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
178  bool lazyEval,
179  const QStringList &aliases,
180  bool handlesNull )
181  : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
182  , mFnc( fcn )
183  , mAliases( aliases )
184  , mUsesGeometry( false )
185  , mUsesGeometryFunc( usesGeometry )
186  , mReferencedColumnsFunc( referencedColumns )
187 {
188 }
190 
192 {
193  return mAliases;
194 }
195 
197 {
198  if ( mUsesGeometryFunc )
199  return mUsesGeometryFunc( node );
200  else
201  return mUsesGeometry;
202 }
203 
205 {
206  if ( mReferencedColumnsFunc )
207  return mReferencedColumnsFunc( node );
208  else
209  return mReferencedColumns;
210 }
211 
213 {
214  if ( mIsStaticFunc )
215  return mIsStaticFunc( node, parent, context );
216  else
217  return mIsStatic;
218 }
219 
221 {
222  if ( mPrepareFunc )
223  return mPrepareFunc( node, parent, context );
224 
225  return true;
226 }
227 
229 {
230  mIsStaticFunc = isStatic;
231 }
232 
234 {
235  mIsStaticFunc = nullptr;
236  mIsStatic = isStatic;
237 }
238 
239 void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
240 {
241  mPrepareFunc = prepareFunc;
242 }
243 
245 {
246  if ( node && node->args() )
247  {
248  const QList< QgsExpressionNode * > argList = node->args()->list();
249  for ( QgsExpressionNode *argNode : argList )
250  {
251  if ( !argNode->isStatic( parent, context ) )
252  return false;
253  }
254  }
255 
256  return true;
257 }
258 
259 static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
260 {
261  double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
262  double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
263  double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
264 
265  if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
266  return QVariant();
267 
268  QVariantList array;
269  int length = 1;
270 
271  array << start;
272  double current = start + step;
273  while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
274  {
275  array << current;
276  current += step;
277  length++;
278  }
279 
280  return array;
281 }
282 
283 static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
284 {
285  if ( !context )
286  return QVariant();
287 
288  QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
289  return context->variable( name );
290 }
291 
292 static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
293 {
294  QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
295  return QgsExpression::replaceExpressionText( templateString, context );
296 }
297 
298 static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
299 {
300  if ( !context )
301  return QVariant();
302 
303  QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
304  QgsExpression expression( expString );
305  return expression.evaluate( context );
306 }
307 
308 static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
309 {
310  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
311  return QVariant( std::sqrt( x ) );
312 }
313 
314 static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
315 {
316  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
317  return QVariant( std::fabs( val ) );
318 }
319 
320 static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
321 {
322  double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
323  return ( deg * M_PI ) / 180;
324 }
325 static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
326 {
327  double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
328  return ( 180 * rad ) / M_PI;
329 }
330 static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
331 {
332  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
333  return QVariant( std::sin( x ) );
334 }
335 static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
336 {
337  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
338  return QVariant( std::cos( x ) );
339 }
340 static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
341 {
342  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
343  return QVariant( std::tan( x ) );
344 }
345 static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
346 {
347  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
348  return QVariant( std::asin( x ) );
349 }
350 static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
351 {
352  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
353  return QVariant( std::acos( x ) );
354 }
355 static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
356 {
357  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
358  return QVariant( std::atan( x ) );
359 }
360 static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
361 {
362  double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
363  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
364  return QVariant( std::atan2( y, x ) );
365 }
366 static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
367 {
368  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
369  return QVariant( std::exp( x ) );
370 }
371 static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
372 {
373  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
374  if ( x <= 0 )
375  return QVariant();
376  return QVariant( std::log( x ) );
377 }
378 static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
379 {
380  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
381  if ( x <= 0 )
382  return QVariant();
383  return QVariant( log10( x ) );
384 }
385 static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
386 {
387  double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
388  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
389  if ( x <= 0 || b <= 0 )
390  return QVariant();
391  return QVariant( std::log( x ) / std::log( b ) );
392 }
393 static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
394 {
395  double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
396  double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
397  if ( max < min )
398  return QVariant();
399 
400  std::random_device rd;
401  std::mt19937_64 generator( rd() );
402 
403  if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
404  {
405  quint32 seed;
406  if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
407  {
408  // if seed can be converted to int, we use as is
409  seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
410  }
411  else
412  {
413  // if not, we hash string representation to int
414  QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
415  std::hash<std::string> hasher;
416  seed = hasher( seedStr.toStdString() );
417  }
418  generator.seed( seed );
419  }
420 
421  // Return a random double in the range [min, max] (inclusive)
422  double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
423  return QVariant( min + f * ( max - min ) );
424 }
425 static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
426 {
427  qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
428  qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
429  if ( max < min )
430  return QVariant();
431 
432  std::random_device rd;
433  std::mt19937_64 generator( rd() );
434 
435  if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
436  {
437  quint32 seed;
438  if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
439  {
440  // if seed can be converted to int, we use as is
441  seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
442  }
443  else
444  {
445  // if not, we hash string representation to int
446  QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
447  std::hash<std::string> hasher;
448  seed = hasher( seedStr.toStdString() );
449  }
450  generator.seed( seed );
451  }
452 
453  qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
454  if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
455  return QVariant( randomInteger );
456 
457  // Prevent wrong conversion of QVariant. See #36412
458  return QVariant( int( randomInteger ) );
459 }
460 
461 static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
462 {
463  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
464  double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
465  double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
466  double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
467  double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
468 
469  if ( domainMin >= domainMax )
470  {
471  parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
472  return QVariant();
473  }
474 
475  // outside of domain?
476  if ( val >= domainMax )
477  {
478  return rangeMax;
479  }
480  else if ( val <= domainMin )
481  {
482  return rangeMin;
483  }
484 
485  // calculate linear scale
486  double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
487  double c = rangeMin - ( domainMin * m );
488 
489  // Return linearly scaled value
490  return QVariant( m * val + c );
491 }
492 
493 static QVariant fcnExpScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
494 {
495  double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
496  double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
497  double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
498  double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
499  double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
500  double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
501 
502  if ( domainMin >= domainMax )
503  {
504  parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
505  return QVariant();
506  }
507  if ( exponent <= 0 )
508  {
509  parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
510  return QVariant();
511  }
512 
513  // outside of domain?
514  if ( val >= domainMax )
515  {
516  return rangeMax;
517  }
518  else if ( val <= domainMin )
519  {
520  return rangeMin;
521  }
522 
523  // Return exponentially scaled value
524  return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
525 }
526 
527 static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
528 {
529  QVariant result( QVariant::Double );
530  double maxVal = std::numeric_limits<double>::quiet_NaN();
531  for ( const QVariant &val : values )
532  {
533  double testVal = val.isNull() ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
534  if ( std::isnan( maxVal ) )
535  {
536  maxVal = testVal;
537  }
538  else if ( !std::isnan( testVal ) )
539  {
540  maxVal = std::max( maxVal, testVal );
541  }
542  }
543 
544  if ( !std::isnan( maxVal ) )
545  {
546  result = QVariant( maxVal );
547  }
548  return result;
549 }
550 
551 static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
552 {
553  QVariant result( QVariant::Double );
554  double minVal = std::numeric_limits<double>::quiet_NaN();
555  for ( const QVariant &val : values )
556  {
557  double testVal = val.isNull() ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
558  if ( std::isnan( minVal ) )
559  {
560  minVal = testVal;
561  }
562  else if ( !std::isnan( testVal ) )
563  {
564  minVal = std::min( minVal, testVal );
565  }
566  }
567 
568  if ( !std::isnan( minVal ) )
569  {
570  result = QVariant( minVal );
571  }
572  return result;
573 }
574 
575 static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
576 {
577  //lazy eval, so we need to evaluate nodes now
578 
579  //first node is layer id or name
580  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
582  QVariant value = node->eval( parent, context );
584  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, parent );
585  if ( !vl )
586  {
587  parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
588  return QVariant();
589  }
590 
591  // second node is aggregate type
592  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
594  value = node->eval( parent, context );
596  bool ok = false;
597  QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
598  if ( !ok )
599  {
600  parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
601  return QVariant();
602  }
603 
604  // third node is subexpression (or field name)
605  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
607  QString subExpression = node->dump();
608 
610  //optional forth node is filter
611  if ( values.count() > 3 )
612  {
613  node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
615  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
616  if ( !nl || nl->value().isValid() )
617  parameters.filter = node->dump();
618  }
619 
620  //optional fifth node is concatenator
621  if ( values.count() > 4 )
622  {
623  node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
625  value = node->eval( parent, context );
627  parameters.delimiter = value.toString();
628  }
629 
630  //optional sixth node is order by
631  QString orderBy;
632  if ( values.count() > 5 )
633  {
634  node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
636  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
637  if ( !nl || nl->value().isValid() )
638  {
639  orderBy = node->dump();
640  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
641  }
642  }
643 
644  QString aggregateError;
645  QVariant result;
646  if ( context )
647  {
648  QString cacheKey;
649  QgsExpression subExp( subExpression );
650  QgsExpression filterExp( parameters.filter );
651 
652  bool isStatic = true;
653  if ( filterExp.referencedVariables().contains( QStringLiteral( "parent" ) )
654  || filterExp.referencedVariables().contains( QString() )
655  || subExp.referencedVariables().contains( QStringLiteral( "parent" ) )
656  || subExp.referencedVariables().contains( QString() ) )
657  {
658  isStatic = false;
659  }
660  else
661  {
662  const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
663  for ( const QString &varName : refVars )
664  {
665  const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
666  if ( scope && !scope->isStatic( varName ) )
667  {
668  isStatic = false;
669  break;
670  }
671  }
672  }
673 
674  if ( !isStatic )
675  {
676  cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter,
677  QString::number( context->feature().id() ), QString::number( qHash( context->feature() ) ), orderBy );
678  }
679  else
680  {
681  cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter, orderBy );
682  }
683 
684  if ( context->hasCachedValue( cacheKey ) )
685  {
686  return context->cachedValue( cacheKey );
687  }
688 
689  QgsExpressionContext subContext( *context );
691  subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
692  subContext.appendScope( subScope );
693  result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
694 
695  if ( ok )
696  {
697  // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
698  // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
699  // associated with it's calculation!
700  context->setCachedValue( cacheKey, result );
701  }
702  }
703  else
704  {
705  result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
706  }
707  if ( !ok )
708  {
709  if ( !aggregateError.isEmpty() )
710  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
711  else
712  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
713  return QVariant();
714  }
715 
716  return result;
717 }
718 
719 static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
720 {
721  if ( !context )
722  {
723  parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
724  return QVariant();
725  }
726 
727  // first step - find current layer
728  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
729  if ( !vl )
730  {
731  parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
732  return QVariant();
733  }
734 
735  //lazy eval, so we need to evaluate nodes now
736 
737  //first node is relation name
738  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
740  QVariant value = node->eval( parent, context );
742  QString relationId = value.toString();
743  // check relation exists
744  QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId );
745  if ( !relation.isValid() || relation.referencedLayer() != vl )
746  {
747  // check for relations by name
748  QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId );
749  if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
750  {
751  parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
752  return QVariant();
753  }
754  else
755  {
756  relation = relations.at( 0 );
757  }
758  }
759 
760  QgsVectorLayer *childLayer = relation.referencingLayer();
761 
762  // second node is aggregate type
763  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
765  value = node->eval( parent, context );
767  bool ok = false;
768  QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
769  if ( !ok )
770  {
771  parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
772  return QVariant();
773  }
774 
775  //third node is subexpression (or field name)
776  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
778  QString subExpression = node->dump();
779 
780  //optional fourth node is concatenator
782  if ( values.count() > 3 )
783  {
784  node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
786  value = node->eval( parent, context );
788  parameters.delimiter = value.toString();
789  }
790 
791  //optional fifth node is order by
792  QString orderBy;
793  if ( values.count() > 4 )
794  {
795  node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
797  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
798  if ( !nl || nl->value().isValid() )
799  {
800  orderBy = node->dump();
801  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
802  }
803  }
804 
805  if ( !context->hasFeature() )
806  return QVariant();
807  QgsFeature f = context->feature();
808 
809  parameters.filter = relation.getRelatedFeaturesFilter( f );
810 
811  QString cacheKey = QStringLiteral( "relagg:%1:%2:%3:%4:%5" ).arg( vl->id(),
812  QString::number( static_cast< int >( aggregate ) ),
813  subExpression,
814  parameters.filter,
815  orderBy );
816  if ( context->hasCachedValue( cacheKey ) )
817  return context->cachedValue( cacheKey );
818 
819  QVariant result;
820  ok = false;
821 
822 
823  QgsExpressionContext subContext( *context );
824  QString error;
825  result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
826 
827  if ( !ok )
828  {
829  if ( !error.isEmpty() )
830  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
831  else
832  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
833  return QVariant();
834  }
835 
836  // cache value
837  context->setCachedValue( cacheKey, result );
838  return result;
839 }
840 
841 
842 static QVariant fcnAggregateGeneric( QgsAggregateCalculator::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
843 {
844  if ( !context )
845  {
846  parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
847  return QVariant();
848  }
849 
850  // first step - find current layer
851  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
852  if ( !vl )
853  {
854  parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
855  return QVariant();
856  }
857 
858  //lazy eval, so we need to evaluate nodes now
859 
860  //first node is subexpression (or field name)
861  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
863  QString subExpression = node->dump();
864 
865  //optional second node is group by
866  QString groupBy;
867  if ( values.count() > 1 )
868  {
869  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
871  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
872  if ( !nl || nl->value().isValid() )
873  groupBy = node->dump();
874  }
875 
876  //optional third node is filter
877  if ( values.count() > 2 )
878  {
879  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
881  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
882  if ( !nl || nl->value().isValid() )
883  parameters.filter = node->dump();
884  }
885 
886  //optional order by node, if supported
887  QString orderBy;
888  if ( orderByPos >= 0 && values.count() > orderByPos )
889  {
890  node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
892  QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
893  if ( !nl || nl->value().isValid() )
894  {
895  orderBy = node->dump();
896  parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
897  }
898  }
899 
900  // build up filter with group by
901 
902  // find current group by value
903  if ( !groupBy.isEmpty() )
904  {
905  QgsExpression groupByExp( groupBy );
906  QVariant groupByValue = groupByExp.evaluate( context );
907  QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
908  groupByValue.isNull() ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
909  QgsExpression::quotedValue( groupByValue ) );
910  if ( !parameters.filter.isEmpty() )
911  parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
912  else
913  parameters.filter = groupByClause;
914  }
915 
916  QgsExpression subExp( subExpression );
917  QgsExpression filterExp( parameters.filter );
918 
919  bool isStatic = true;
920  const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
921  for ( const QString &varName : refVars )
922  {
923  const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
924  if ( scope && !scope->isStatic( varName ) )
925  {
926  isStatic = false;
927  break;
928  }
929  }
930 
931  QString cacheKey;
932  if ( !isStatic )
933  {
934  cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter,
935  QString::number( context->feature().id() ), QString::number( qHash( context->feature() ) ), orderBy );
936  }
937  else
938  {
939  cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter, orderBy );
940  }
941 
942  if ( context->hasCachedValue( cacheKey ) )
943  return context->cachedValue( cacheKey );
944 
945  QVariant result;
946  bool ok = false;
947 
948  QgsExpressionContext subContext( *context );
950  subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
951  subContext.appendScope( subScope );
952  QString error;
953  result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
954 
955  if ( !ok )
956  {
957  if ( !error.isEmpty() )
958  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
959  else
960  parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
961  return QVariant();
962  }
963 
964  // cache value
965  context->setCachedValue( cacheKey, result );
966  return result;
967 }
968 
969 
970 static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
971 {
972  return fcnAggregateGeneric( QgsAggregateCalculator::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
973 }
974 
975 static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
976 {
977  return fcnAggregateGeneric( QgsAggregateCalculator::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
978 }
979 
980 static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
981 {
982  return fcnAggregateGeneric( QgsAggregateCalculator::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
983 }
984 
985 static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
986 {
987  return fcnAggregateGeneric( QgsAggregateCalculator::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
988 }
989 
990 static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
991 {
992  return fcnAggregateGeneric( QgsAggregateCalculator::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
993 }
994 
995 static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
996 {
997  return fcnAggregateGeneric( QgsAggregateCalculator::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
998 }
999 
1000 static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1001 {
1002  return fcnAggregateGeneric( QgsAggregateCalculator::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1003 }
1004 
1005 static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1006 {
1007  return fcnAggregateGeneric( QgsAggregateCalculator::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1008 }
1009 
1010 static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1011 {
1012  return fcnAggregateGeneric( QgsAggregateCalculator::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1013 }
1014 
1015 static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1016 {
1017  return fcnAggregateGeneric( QgsAggregateCalculator::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1018 }
1019 
1020 static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1021 {
1022  return fcnAggregateGeneric( QgsAggregateCalculator::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1023 }
1024 
1025 static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1026 {
1027  return fcnAggregateGeneric( QgsAggregateCalculator::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1028 }
1029 
1030 static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1031 {
1032  return fcnAggregateGeneric( QgsAggregateCalculator::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1033 }
1034 
1035 static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1036 {
1037  return fcnAggregateGeneric( QgsAggregateCalculator::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1038 }
1039 
1040 static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1041 {
1042  return fcnAggregateGeneric( QgsAggregateCalculator::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1043 }
1044 
1045 static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1046 {
1047  return fcnAggregateGeneric( QgsAggregateCalculator::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1048 }
1049 
1050 static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1051 {
1052  return fcnAggregateGeneric( QgsAggregateCalculator::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1053 }
1054 
1055 static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1056 {
1057  return fcnAggregateGeneric( QgsAggregateCalculator::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1058 }
1059 
1060 static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1061 {
1063 
1064  //fourth node is concatenator
1065  if ( values.count() > 3 )
1066  {
1067  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1069  QVariant value = node->eval( parent, context );
1071  parameters.delimiter = value.toString();
1072  }
1073 
1074  return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenate, values, parameters, context, parent, 4 );
1075 }
1076 
1077 static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1078 {
1080 
1081  //fourth node is concatenator
1082  if ( values.count() > 3 )
1083  {
1084  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1086  QVariant value = node->eval( parent, context );
1088  parameters.delimiter = value.toString();
1089  }
1090 
1091  return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenateUnique, values, parameters, context, parent, 4 );
1092 }
1093 
1094 static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1095 {
1096  return fcnAggregateGeneric( QgsAggregateCalculator::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1097 }
1098 
1099 static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1100 {
1101  if ( !context )
1102  return QVariant();
1103 
1104  QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1105  bool ok = false;
1106  if ( !scale.isValid() || scale.isNull() )
1107  return QVariant();
1108 
1109  const double v = scale.toDouble( &ok );
1110  if ( ok )
1111  return v;
1112  return QVariant();
1113 }
1114 
1115 static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1116 {
1117  double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1118  double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1119  double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1120 
1121  // force testValue to sit inside the range specified by the min and max value
1122  if ( testValue <= minValue )
1123  {
1124  return QVariant( minValue );
1125  }
1126  else if ( testValue >= maxValue )
1127  {
1128  return QVariant( maxValue );
1129  }
1130  else
1131  {
1132  return QVariant( testValue );
1133  }
1134 }
1135 
1136 static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1137 {
1138  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1139  return QVariant( std::floor( x ) );
1140 }
1141 
1142 static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1143 {
1144  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1145  return QVariant( std::ceil( x ) );
1146 }
1147 
1148 static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1149 {
1150  return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1151 }
1152 static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1153 {
1154  return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1155 }
1156 static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1157 {
1158  return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1159 }
1160 
1161 static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1162 {
1163  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1164  QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1165  if ( format.isEmpty() && !language.isEmpty() )
1166  {
1167  parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1168  return QVariant( QDateTime() );
1169  }
1170 
1171  if ( format.isEmpty() && language.isEmpty() )
1172  return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1173 
1174  QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1175  QLocale locale = QLocale();
1176  if ( !language.isEmpty() )
1177  {
1178  locale = QLocale( language );
1179  }
1180 
1181  QDateTime datetime = locale.toDateTime( datetimestring, format );
1182  if ( !datetime.isValid() )
1183  {
1184  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1185  datetime = QDateTime();
1186  }
1187  return QVariant( datetime );
1188 }
1189 
1190 static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1191 {
1192  const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1193  const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1194  const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1195 
1196  const QDate date( year, month, day );
1197  if ( !date.isValid() )
1198  {
1199  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1200  return QVariant();
1201  }
1202  return QVariant( date );
1203 }
1204 
1205 static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1206 {
1207  const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1208  const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1209  const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1210 
1211  const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1212  if ( !time.isValid() )
1213  {
1214  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1215  return QVariant();
1216  }
1217  return QVariant( time );
1218 }
1219 
1220 static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1221 {
1222  const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1223  const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1224  const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1225  const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1226  const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1227  const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1228 
1229  const QDate date( year, month, day );
1230  if ( !date.isValid() )
1231  {
1232  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1233  return QVariant();
1234  }
1235  const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1236  if ( !time.isValid() )
1237  {
1238  parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1239  return QVariant();
1240  }
1241  return QVariant( QDateTime( date, time ) );
1242 }
1243 
1244 static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1245 {
1246  const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1247  const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1248  const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1249  const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1250  const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1251  const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1252  const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1253 
1254  return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1255 }
1256 
1257 static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1258 {
1259  for ( const QVariant &value : values )
1260  {
1261  if ( value.isNull() )
1262  continue;
1263  return value;
1264  }
1265  return QVariant();
1266 }
1267 
1268 static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1269 {
1270  const QVariant val1 = values.at( 0 );
1271  const QVariant val2 = values.at( 1 );
1272 
1273  if ( val1 == val2 )
1274  return QVariant();
1275  else
1276  return val1;
1277 }
1278 
1279 static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1280 {
1281  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1282  return QVariant( str.toLower() );
1283 }
1284 static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1285 {
1286  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1287  return QVariant( str.toUpper() );
1288 }
1289 static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1290 {
1291  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1292  QStringList elems = str.split( ' ' );
1293  for ( int i = 0; i < elems.size(); i++ )
1294  {
1295  if ( elems[i].size() > 1 )
1296  elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1297  }
1298  return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1299 }
1300 
1301 static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1302 {
1303  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1304  return QVariant( str.trimmed() );
1305 }
1306 
1307 static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1308 {
1309  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1310  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1311  return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1312 }
1313 
1314 static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1315 {
1316  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1317  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1318  return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1319 }
1320 
1321 static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1322 {
1323  QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1324  QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1325  int dist = QgsStringUtils::hammingDistance( string1, string2 );
1326  return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1327 }
1328 
1329 static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1330 {
1331  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1332  return QVariant( QgsStringUtils::soundex( string ) );
1333 }
1334 
1335 static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1336 {
1337  QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1338  return QVariant( QString( character ) );
1339 }
1340 
1341 static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1342 {
1343  QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1344 
1345  if ( value.isEmpty() )
1346  {
1347  return QVariant();
1348  }
1349 
1350  int res = value.at( 0 ).unicode();
1351  return QVariant( res );
1352 }
1353 
1354 static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1355 {
1356  if ( values.length() == 2 || values.length() == 3 )
1357  {
1358  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1359  qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1360 
1361  QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1362 
1363  return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1364  }
1365 
1366  return QVariant();
1367 }
1368 
1369 static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1370 {
1371  // two variants, one for geometry, one for string
1372  if ( values.at( 0 ).canConvert<QgsGeometry>() )
1373  {
1374  //geometry variant
1375  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1376  if ( geom.type() != QgsWkbTypes::LineGeometry )
1377  return QVariant();
1378 
1379  return QVariant( geom.length() );
1380  }
1381 
1382  //otherwise fall back to string variant
1383  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1384  return QVariant( str.length() );
1385 }
1386 
1387 static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1388 {
1389  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1390 
1391  if ( geom.type() != QgsWkbTypes::LineGeometry )
1392  return QVariant();
1393 
1394  double totalLength = 0;
1395  for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1396  {
1397  if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *it ) )
1398  {
1399  totalLength += line->length3D();
1400  }
1401  else
1402  {
1403  std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1404  totalLength += segmentized->length3D();
1405  }
1406  }
1407 
1408  return totalLength;
1409 }
1410 
1411 static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1412 {
1413  if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
1414  {
1415  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1416  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1417  QVector< QPair< QString, QString > > mapItems;
1418 
1419  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1420  {
1421  mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1422  }
1423 
1424  // larger keys should be replaced first since they may contain whole smaller keys
1425  std::sort( mapItems.begin(),
1426  mapItems.end(),
1427  []( const QPair< QString, QString > &pair1,
1428  const QPair< QString, QString > &pair2 )
1429  {
1430  return ( pair1.first.length() > pair2.first.length() );
1431  } );
1432 
1433  for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1434  {
1435  str = str.replace( it->first, it->second );
1436  }
1437 
1438  return QVariant( str );
1439  }
1440  else if ( values.count() == 3 )
1441  {
1442  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1443  QVariantList before;
1444  QVariantList after;
1445  bool isSingleReplacement = false;
1446 
1447  if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).type() != QVariant::StringList )
1448  {
1449  before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1450  }
1451  else
1452  {
1453  before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1454  }
1455 
1456  if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1457  {
1458  after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1459  isSingleReplacement = true;
1460  }
1461  else
1462  {
1463  after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1464  }
1465 
1466  if ( !isSingleReplacement && before.length() != after.length() )
1467  {
1468  parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1469  return QVariant();
1470  }
1471 
1472  for ( int i = 0; i < before.length(); i++ )
1473  {
1474  str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1475  }
1476 
1477  return QVariant( str );
1478  }
1479  else
1480  {
1481  parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1482  return QVariant();
1483  }
1484 }
1485 
1486 static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1487 {
1488  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1489  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1490  QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1491 
1492  QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1493  if ( !re.isValid() )
1494  {
1495  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1496  return QVariant();
1497  }
1498  return QVariant( str.replace( re, after ) );
1499 }
1500 
1501 static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1502 {
1503  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1504  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1505 
1506  QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1507  if ( !re.isValid() )
1508  {
1509  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1510  return QVariant();
1511  }
1512  return QVariant( ( str.indexOf( re ) + 1 ) );
1513 }
1514 
1515 static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1516 {
1517  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1518  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1519  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1520 
1521  QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1522  if ( !re.isValid() )
1523  {
1524  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1525  return QVariant();
1526  }
1527 
1528  QRegularExpressionMatch matches = re.match( str );
1529  if ( matches.hasMatch() )
1530  {
1531  QVariantList array;
1532  QStringList list = matches.capturedTexts();
1533 
1534  // Skip the first string to only return captured groups
1535  for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1536  {
1537  array += ( !( *it ).isEmpty() ) ? *it : empty;
1538  }
1539 
1540  return QVariant( array );
1541  }
1542  else
1543  {
1544  return QVariant();
1545  }
1546 }
1547 
1548 static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1549 {
1550  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1551  QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1552 
1553  QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1554  if ( !re.isValid() )
1555  {
1556  parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1557  return QVariant();
1558  }
1559 
1560  // extract substring
1561  QRegularExpressionMatch match = re.match( str );
1562  if ( match.hasMatch() )
1563  {
1564  // return first capture
1565  if ( match.lastCapturedIndex() > 0 )
1566  {
1567  // a capture group was present, so use that
1568  return QVariant( match.captured( 1 ) );
1569  }
1570  else
1571  {
1572  // no capture group, so using all match
1573  return QVariant( match.captured( 0 ) );
1574  }
1575  }
1576  else
1577  {
1578  return QVariant( "" );
1579  }
1580 }
1581 
1582 static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1583 {
1584  QString uuid = QUuid::createUuid().toString();
1585  if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1586  uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1587  else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1588  uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1589  return uuid;
1590 }
1591 
1592 static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1593 {
1594  if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1595  return QVariant();
1596 
1597  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1598  int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1599 
1600  int len = 0;
1601  if ( values.at( 2 ).isValid() )
1602  len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1603  else
1604  len = str.size();
1605 
1606  if ( from < 0 )
1607  {
1608  from = str.size() + from;
1609  if ( from < 0 )
1610  {
1611  from = 0;
1612  }
1613  }
1614  else if ( from > 0 )
1615  {
1616  //account for the fact that substr() starts at 1
1617  from -= 1;
1618  }
1619 
1620  if ( len < 0 )
1621  {
1622  len = str.size() + len - from;
1623  if ( len < 0 )
1624  {
1625  len = 0;
1626  }
1627  }
1628 
1629  return QVariant( str.mid( from, len ) );
1630 }
1631 static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1632 {
1633  FEAT_FROM_CONTEXT( context, f )
1634  // TODO: handling of 64-bit feature ids?
1635  return QVariant( static_cast< int >( f.id() ) );
1636 }
1637 
1638 static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1639 {
1640  QgsRasterLayer *layer = QgsExpressionUtils::getRasterLayer( values.at( 0 ), parent );
1641  if ( !layer || !layer->dataProvider() )
1642  {
1643  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1644  return QVariant();
1645  }
1646 
1647  int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1648  if ( bandNb < 1 || bandNb > layer->bandCount() )
1649  {
1650  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1651  return QVariant();
1652  }
1653 
1654  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1655  if ( geom.isNull() || geom.type() != QgsWkbTypes::PointGeometry )
1656  {
1657  parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1658  return QVariant();
1659  }
1660 
1661  QgsPointXY point = geom.asPoint();
1662  if ( geom.isMultipart() )
1663  {
1664  QgsMultiPointXY multiPoint = geom.asMultiPoint();
1665  if ( multiPoint.count() == 1 )
1666  {
1667  point = multiPoint[0];
1668  }
1669  else
1670  {
1671  // if the geometry contains more than one part, return an undefined value
1672  return QVariant();
1673  }
1674  }
1675 
1676  double value = layer->dataProvider()->sample( point, bandNb );
1677  return std::isnan( value ) ? QVariant() : value;
1678 }
1679 
1680 static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1681 {
1682  if ( !context )
1683  return QVariant();
1684 
1685  return context->feature();
1686 }
1687 
1688 static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1689 {
1690  QgsFeature feature;
1691  QString attr;
1692  if ( values.size() == 1 )
1693  {
1694  attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1695  feature = context->feature();
1696  }
1697  else if ( values.size() == 2 )
1698  {
1699  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1700  attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1701  }
1702  else
1703  {
1704  parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
1705  return QVariant();
1706  }
1707 
1708  return feature.attribute( attr );
1709 }
1710 
1711 static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1712 {
1713  QgsFeature feature;
1714  if ( values.size() == 0 || values.at( 0 ).isNull() )
1715  {
1716  feature = context->feature();
1717  }
1718  else
1719  {
1720  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1721  }
1722 
1723  const QgsFields fields = feature.fields();
1724  QVariantMap result;
1725  for ( int i = 0; i < fields.count(); ++i )
1726  {
1727  result.insert( fields.at( i ).name(), feature.attribute( i ) );
1728  }
1729  return result;
1730 }
1731 
1732 static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1733 {
1734  QgsVectorLayer *layer = nullptr;
1735  QgsFeature feature;
1736 
1737  if ( values.isEmpty() )
1738  {
1739  feature = context->feature();
1740  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1741  }
1742  else if ( values.size() == 1 )
1743  {
1744  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1745  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1746  }
1747  else if ( values.size() == 2 )
1748  {
1749  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1750  feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1751  }
1752  else
1753  {
1754  parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
1755  return QVariant();
1756  }
1757 
1758  if ( !layer )
1759  {
1760  parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
1761  return QVariant();
1762  }
1763 
1764  if ( !feature.isValid() )
1765  {
1766  parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
1767  return QVariant();
1768  }
1769 
1770  const QgsFields fields = feature.fields();
1771  QVariantMap result;
1772  for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
1773  {
1774  const QString fieldName { fields.at( fieldIndex ).name() };
1775  const QVariant attributeVal = feature.attribute( fieldIndex );
1776  const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
1777  if ( context && context->hasCachedValue( cacheValueKey ) )
1778  {
1779  result.insert( fieldName, context->cachedValue( cacheValueKey ) );
1780  }
1781  else
1782  {
1783  const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
1785  QVariant cache;
1786  if ( context )
1787  {
1788  const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
1789 
1790  if ( !context->hasCachedValue( cacheKey ) )
1791  {
1792  cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
1793  context->setCachedValue( cacheKey, cache );
1794  }
1795  else
1796  {
1797  cache = context->cachedValue( cacheKey );
1798  }
1799  }
1800  QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
1801 
1802  result.insert( fields.at( fieldIndex ).name(), value );
1803 
1804  if ( context )
1805  {
1806  context->setCachedValue( cacheValueKey, value );
1807  }
1808 
1809  }
1810  }
1811  return result;
1812 }
1813 
1814 static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
1815 {
1816  QgsVectorLayer *layer = nullptr;
1817  QgsFeature feature;
1818  bool evaluate = true;
1819 
1820  if ( values.isEmpty() )
1821  {
1822  feature = context->feature();
1823  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1824  }
1825  else if ( values.size() == 1 )
1826  {
1827  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1828  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1829  }
1830  else if ( values.size() == 2 )
1831  {
1832  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1833  feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1834  }
1835  else if ( values.size() == 3 )
1836  {
1837  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1838  feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1839  evaluate = values.value( 2 ).toBool();
1840  }
1841  else
1842  {
1843  if ( isMaptip )
1844  {
1845  parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
1846  }
1847  else
1848  {
1849  parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
1850  }
1851  return QVariant();
1852  }
1853 
1854  if ( !layer )
1855  {
1856  parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
1857  return QVariant( );
1858  }
1859 
1860  if ( !feature.isValid() )
1861  {
1862  parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
1863  return QVariant( );
1864  }
1865 
1866  if ( ! evaluate )
1867  {
1868  if ( isMaptip )
1869  {
1870  return layer->mapTipTemplate();
1871  }
1872  else
1873  {
1874  return layer->displayExpression();
1875  }
1876  }
1877 
1878  QgsExpressionContext subContext( *context );
1879  subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
1880  subContext.setFeature( feature );
1881 
1882  if ( isMaptip )
1883  {
1884  return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
1885  }
1886  else
1887  {
1888  QgsExpression exp( layer->displayExpression() );
1889  exp.prepare( &subContext );
1890  return exp.evaluate( &subContext ).toString();
1891  }
1892 }
1893 
1894 static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1895 {
1896  return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
1897 }
1898 
1899 static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1900 {
1901  return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
1902 }
1903 
1904 static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1905 {
1906  QgsVectorLayer *layer = nullptr;
1907  QgsFeature feature;
1908 
1909  if ( values.isEmpty() )
1910  {
1911  feature = context->feature();
1912  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1913  }
1914  else if ( values.size() == 1 )
1915  {
1916  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1917  feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1918  }
1919  else if ( values.size() == 2 )
1920  {
1921  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1922  feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1923  }
1924  else
1925  {
1926  parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
1927  return QVariant();
1928  }
1929 
1930  if ( !layer || !feature.isValid() )
1931  {
1932  return QVariant( QVariant::Bool );
1933  }
1934 
1935  return layer->selectedFeatureIds().contains( feature.id() );
1936 }
1937 
1938 static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1939 {
1940  QgsVectorLayer *layer = nullptr;
1941 
1942  if ( values.isEmpty() )
1943  layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1944  else if ( values.count() == 1 )
1945  layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1946  else
1947  {
1948  parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
1949  return QVariant();
1950  }
1951 
1952  if ( !layer )
1953  {
1954  return QVariant( QVariant::LongLong );
1955  }
1956 
1957  return layer->selectedFeatureCount();
1958 }
1959 
1960 static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1961 {
1962  static QMap<QString, qlonglong> counterCache;
1963  QVariant functionResult;
1964 
1965  std::function<void()> fetchAndIncrementFunc = [ =, &functionResult ]()
1966  {
1967  QString database;
1968  const QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1969 
1970  if ( layer )
1971  {
1972  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
1973  database = decodedUri.value( QStringLiteral( "path" ) ).toString();
1974  if ( database.isEmpty() )
1975  {
1976  parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
1977  }
1978  }
1979  else
1980  {
1981  database = values.at( 0 ).toString();
1982  }
1983 
1984  const QString table = values.at( 1 ).toString();
1985  const QString idColumn = values.at( 2 ).toString();
1986  const QString filterAttribute = values.at( 3 ).toString();
1987  const QVariant filterValue = values.at( 4 ).toString();
1988  const QVariantMap defaultValues = values.at( 5 ).toMap();
1989 
1990  // read from database
1991  sqlite3_database_unique_ptr sqliteDb;
1992  sqlite3_statement_unique_ptr sqliteStatement;
1993 
1994  if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
1995  {
1996  parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
1997  functionResult = QVariant();
1998  return;
1999  }
2000 
2001  QString errorMessage;
2002  QString currentValSql;
2003 
2004  qlonglong nextId = 0;
2005  bool cachedMode = false;
2006  bool valueRetrieved = false;
2007 
2008  QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2009 
2010  // Running in transaction mode, check for cached value first
2011  if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2012  {
2013  cachedMode = true;
2014 
2015  auto cachedCounter = counterCache.find( cacheString );
2016 
2017  if ( cachedCounter != counterCache.end() )
2018  {
2019  qlonglong &cachedValue = cachedCounter.value();
2020  nextId = cachedValue;
2021  nextId += 1;
2022  cachedValue = nextId;
2023  valueRetrieved = true;
2024  }
2025  }
2026 
2027  // Either not in cached mode or no cached value found, obtain from DB
2028  if ( !cachedMode || !valueRetrieved )
2029  {
2030  int result = SQLITE_ERROR;
2031 
2032  currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2033  if ( !filterAttribute.isNull() )
2034  {
2035  currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2036  }
2037 
2038  sqliteStatement = sqliteDb.prepare( currentValSql, result );
2039 
2040  if ( result == SQLITE_OK )
2041  {
2042  nextId = 0;
2043  if ( sqliteStatement.step() == SQLITE_ROW )
2044  {
2045  nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2046  }
2047 
2048  // If in cached mode: add value to cache and connect to transaction
2049  if ( cachedMode && result == SQLITE_OK )
2050  {
2051  counterCache.insert( cacheString, nextId );
2052 
2053  QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2054  {
2055  counterCache.remove( cacheString );
2056  } );
2057  }
2058  valueRetrieved = true;
2059  }
2060  }
2061 
2062  if ( valueRetrieved )
2063  {
2064  QString upsertSql;
2065  upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
2066  QStringList cols;
2067  QStringList vals;
2068  cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2069  vals << QgsSqliteUtils::quotedValue( nextId );
2070 
2071  if ( !filterAttribute.isNull() )
2072  {
2073  cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2074  vals << QgsSqliteUtils::quotedValue( filterValue );
2075  }
2076 
2077  for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2078  {
2079  cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2080  vals << iter.value().toString();
2081  }
2082 
2083  upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
2084  upsertSql += QLatin1String( " VALUES " );
2085  upsertSql += '(' + vals.join( ',' ) + ')';
2086 
2087  int result = SQLITE_ERROR;
2088  if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2089  {
2090  QgsTransaction *transaction = layer->dataProvider()->transaction();
2091  if ( transaction->executeSql( upsertSql, errorMessage ) )
2092  {
2093  result = SQLITE_OK;
2094  }
2095  }
2096  else
2097  {
2098  result = sqliteDb.exec( upsertSql, errorMessage );
2099  }
2100  if ( result == SQLITE_OK )
2101  {
2102  functionResult = QVariant( nextId );
2103  return;
2104  }
2105  else
2106  {
2107  parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2108  functionResult = QVariant();
2109  return;
2110  }
2111  }
2112 
2113  functionResult = QVariant();
2114  };
2115 
2116  QgsThreadingUtils::runOnMainThread( fetchAndIncrementFunc );
2117 
2118  return functionResult;
2119 }
2120 
2121 static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2122 {
2123  QString concat;
2124  for ( const QVariant &value : values )
2125  {
2126  if ( !value.isNull() )
2127  concat += QgsExpressionUtils::getStringValue( value, parent );
2128  }
2129  return concat;
2130 }
2131 
2132 static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2133 {
2134  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2135  return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2136 }
2137 
2138 static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2139 {
2140  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2141  int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2142  return string.right( pos );
2143 }
2144 
2145 static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2146 {
2147  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2148  int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2149  return string.left( pos );
2150 }
2151 
2152 static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2153 {
2154  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2155  int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2156  QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2157  return string.leftJustified( length, fill.at( 0 ), true );
2158 }
2159 
2160 static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2161 {
2162  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2163  int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2164  QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2165  return string.rightJustified( length, fill.at( 0 ), true );
2166 }
2167 
2168 static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2169 {
2170  if ( values.size() < 1 )
2171  {
2172  parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2173  return QVariant();
2174  }
2175 
2176  QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2177  for ( int n = 1; n < values.length(); n++ )
2178  {
2179  string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2180  }
2181  return string;
2182 }
2183 
2184 
2185 static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2186 {
2187  return QVariant( QDateTime::currentDateTime() );
2188 }
2189 
2190 static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2191 {
2192  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2193  QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2194  if ( format.isEmpty() && !language.isEmpty() )
2195  {
2196  parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2197  return QVariant( QDate() );
2198  }
2199 
2200  if ( format.isEmpty() && language.isEmpty() )
2201  return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2202 
2203  QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2204  QLocale locale = QLocale();
2205  if ( !language.isEmpty() )
2206  {
2207  locale = QLocale( language );
2208  }
2209 
2210  QDate date = locale.toDate( datestring, format );
2211  if ( !date.isValid() )
2212  {
2213  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2214  date = QDate();
2215  }
2216  return QVariant( date );
2217 }
2218 
2219 static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2220 {
2221  QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2222  QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2223  if ( format.isEmpty() && !language.isEmpty() )
2224  {
2225  parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2226  return QVariant( QTime() );
2227  }
2228 
2229  if ( format.isEmpty() && language.isEmpty() )
2230  return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2231 
2232  QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2233  QLocale locale = QLocale();
2234  if ( !language.isEmpty() )
2235  {
2236  locale = QLocale( language );
2237  }
2238 
2239  QTime time = locale.toTime( timestring, format );
2240  if ( !time.isValid() )
2241  {
2242  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2243  time = QTime();
2244  }
2245  return QVariant( time );
2246 }
2247 
2248 static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2249 {
2250  return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2251 }
2252 
2253 /*
2254  * DMS functions
2255  */
2256 
2257 static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2258 {
2259  double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2260  QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2261  int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2262 
2263  QString formatString;
2264  if ( values.count() > 3 )
2265  formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2266 
2267  QgsCoordinateFormatter::FormatFlags flags = QgsCoordinateFormatter::FormatFlags();
2268  if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2269  {
2271  }
2272  else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2273  {
2275  }
2276  else if ( ! formatString.isEmpty() )
2277  {
2278  parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2279  return QVariant();
2280  }
2281 
2282  if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2283  {
2284  return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2285  }
2286  else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2287  {
2288  return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2289  }
2290  else
2291  {
2292  parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2293  return QVariant();
2294  }
2295 }
2296 
2297 static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2298 {
2300  return floatToDegreeFormat( format, values, context, parent, node );
2301 }
2302 
2303 static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2304 {
2305  double value = 0.0;
2306  bool ok = false;
2307  value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2308 
2309  return ok ? QVariant( value ) : QVariant();
2310 }
2311 
2312 static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2313 {
2315  return floatToDegreeFormat( format, values, context, parent, node );
2316 }
2317 
2318 static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2319 {
2320  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2321  QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2322  qint64 seconds = d2.secsTo( d1 );
2323  return QVariant::fromValue( QgsInterval( seconds ) );
2324 }
2325 
2326 static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2327 {
2328  if ( !values.at( 0 ).canConvert<QDate>() )
2329  return QVariant();
2330 
2331  QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2332  if ( !date.isValid() )
2333  return QVariant();
2334 
2335  // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2336  // (to match PostgreSQL behavior)
2337  return date.dayOfWeek() % 7;
2338 }
2339 
2340 static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2341 {
2342  QVariant value = values.at( 0 );
2343  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2344  if ( inter.isValid() )
2345  {
2346  return QVariant( inter.days() );
2347  }
2348  else
2349  {
2350  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2351  return QVariant( d1.date().day() );
2352  }
2353 }
2354 
2355 static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2356 {
2357  QVariant value = values.at( 0 );
2358  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2359  if ( inter.isValid() )
2360  {
2361  return QVariant( inter.years() );
2362  }
2363  else
2364  {
2365  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2366  return QVariant( d1.date().year() );
2367  }
2368 }
2369 
2370 static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2371 {
2372  QVariant value = values.at( 0 );
2373  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2374  if ( inter.isValid() )
2375  {
2376  return QVariant( inter.months() );
2377  }
2378  else
2379  {
2380  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2381  return QVariant( d1.date().month() );
2382  }
2383 }
2384 
2385 static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2386 {
2387  QVariant value = values.at( 0 );
2388  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2389  if ( inter.isValid() )
2390  {
2391  return QVariant( inter.weeks() );
2392  }
2393  else
2394  {
2395  QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2396  return QVariant( d1.date().weekNumber() );
2397  }
2398 }
2399 
2400 static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2401 {
2402  QVariant value = values.at( 0 );
2403  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2404  if ( inter.isValid() )
2405  {
2406  return QVariant( inter.hours() );
2407  }
2408  else
2409  {
2410  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2411  return QVariant( t1.hour() );
2412  }
2413 }
2414 
2415 static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2416 {
2417  QVariant value = values.at( 0 );
2418  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2419  if ( inter.isValid() )
2420  {
2421  return QVariant( inter.minutes() );
2422  }
2423  else
2424  {
2425  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2426  return QVariant( t1.minute() );
2427  }
2428 }
2429 
2430 static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2431 {
2432  QVariant value = values.at( 0 );
2433  QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2434  if ( inter.isValid() )
2435  {
2436  return QVariant( inter.seconds() );
2437  }
2438  else
2439  {
2440  QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2441  return QVariant( t1.second() );
2442  }
2443 }
2444 
2445 static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2446 {
2447  QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2448  if ( dt.isValid() )
2449  {
2450  return QVariant( dt.toMSecsSinceEpoch() );
2451  }
2452  else
2453  {
2454  return QVariant();
2455  }
2456 }
2457 
2458 static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2459 {
2460  long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2461  // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2462  return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2463 }
2464 
2465 static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2466 {
2467  const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
2468  if ( parent->hasEvalError() )
2469  {
2470  parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif" ) ) );
2471  return QVariant();
2472  }
2473  QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2474  return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
2475 }
2476 
2477 static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2478 {
2479  const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
2480  if ( parent->hasEvalError() )
2481  {
2482  parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif_geotag" ) ) );
2483  return QVariant();
2484  }
2485  bool ok;
2486  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
2487 }
2488 
2489 #define ENSURE_GEOM_TYPE(f, g, geomtype) \
2490  if ( !(f).hasGeometry() ) \
2491  return QVariant(); \
2492  QgsGeometry g = (f).geometry(); \
2493  if ( (g).type() != (geomtype) ) \
2494  return QVariant();
2495 
2496 static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2497 {
2498  FEAT_FROM_CONTEXT( context, f )
2500  if ( g.isMultipart() )
2501  {
2502  return g.asMultiPoint().at( 0 ).x();
2503  }
2504  else
2505  {
2506  return g.asPoint().x();
2507  }
2508 }
2509 
2510 static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2511 {
2512  FEAT_FROM_CONTEXT( context, f )
2514  if ( g.isMultipart() )
2515  {
2516  return g.asMultiPoint().at( 0 ).y();
2517  }
2518  else
2519  {
2520  return g.asPoint().y();
2521  }
2522 }
2523 
2524 static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2525 {
2526  FEAT_FROM_CONTEXT( context, f )
2528 
2529  if ( g.isEmpty() )
2530  return QVariant();
2531 
2532  const QgsAbstractGeometry *abGeom = g.constGet();
2533 
2534  if ( g.isEmpty() || !abGeom->is3D() )
2535  return QVariant();
2536 
2537  if ( g.type() == QgsWkbTypes::PointGeometry && !g.isMultipart() )
2538  {
2539  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
2540  if ( point )
2541  return point->z();
2542  }
2543  else if ( g.type() == QgsWkbTypes::PointGeometry && g.isMultipart() )
2544  {
2545  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
2546  {
2547  if ( collection->numGeometries() > 0 )
2548  {
2549  if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2550  return point->z();
2551  }
2552  }
2553  }
2554 
2555  return QVariant();
2556 }
2557 
2558 static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2559 {
2560  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2561  if ( geom.isNull() )
2562  return QVariant();
2563 
2564  bool isValid = geom.isGeosValid();
2565 
2566  return QVariant( isValid );
2567 }
2568 
2569 static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2570 {
2571  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2572  if ( geom.isNull() )
2573  return QVariant();
2574 
2575  //if single point, return the point's x coordinate
2576  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2577  {
2578  return geom.asPoint().x();
2579  }
2580 
2581  //otherwise return centroid x
2582  QgsGeometry centroid = geom.centroid();
2583  QVariant result( centroid.asPoint().x() );
2584  return result;
2585 }
2586 
2587 static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2588 {
2589  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2590  if ( geom.isNull() )
2591  return QVariant();
2592 
2593  //if single point, return the point's y coordinate
2594  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2595  {
2596  return geom.asPoint().y();
2597  }
2598 
2599  //otherwise return centroid y
2600  QgsGeometry centroid = geom.centroid();
2601  QVariant result( centroid.asPoint().y() );
2602  return result;
2603 }
2604 
2605 static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2606 {
2607  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2608  if ( geom.isNull() )
2609  return QVariant(); //or 0?
2610 
2611  if ( !geom.constGet()->is3D() )
2612  return QVariant();
2613 
2614  //if single point, return the point's z coordinate
2615  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2616  {
2617  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2618  if ( point )
2619  return point->z();
2620  }
2621  else if ( geom.type() == QgsWkbTypes::PointGeometry && geom.isMultipart() )
2622  {
2623  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2624  {
2625  if ( collection->numGeometries() == 1 )
2626  {
2627  if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2628  return point->z();
2629  }
2630  }
2631  }
2632 
2633  return QVariant();
2634 }
2635 
2636 static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2637 {
2638  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2639  if ( geom.isNull() )
2640  return QVariant(); //or 0?
2641 
2642  if ( !geom.constGet()->isMeasure() )
2643  return QVariant();
2644 
2645  //if single point, return the point's m value
2646  if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2647  {
2648  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2649  if ( point )
2650  return point->m();
2651  }
2652  else if ( geom.type() == QgsWkbTypes::PointGeometry && geom.isMultipart() )
2653  {
2654  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2655  {
2656  if ( collection->numGeometries() == 1 )
2657  {
2658  if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2659  return point->m();
2660  }
2661  }
2662  }
2663 
2664  return QVariant();
2665 }
2666 
2667 static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2668 {
2669  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2670 
2671  if ( geom.isNull() )
2672  return QVariant();
2673 
2674  int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2675 
2676  if ( idx < 0 )
2677  {
2678  //negative idx
2679  int count = geom.constGet()->nCoordinates();
2680  idx = count + idx;
2681  }
2682  else
2683  {
2684  //positive idx is 1 based
2685  idx -= 1;
2686  }
2687 
2688  QgsVertexId vId;
2689  if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
2690  {
2691  parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
2692  return QVariant();
2693  }
2694 
2695  QgsPoint point = geom.constGet()->vertexAt( vId );
2696  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2697 }
2698 
2699 static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2700 {
2701  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2702 
2703  if ( geom.isNull() )
2704  return QVariant();
2705 
2706  QgsVertexId vId;
2707  if ( !geom.vertexIdFromVertexNr( 0, vId ) )
2708  {
2709  return QVariant();
2710  }
2711 
2712  QgsPoint point = geom.constGet()->vertexAt( vId );
2713  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2714 }
2715 
2716 static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2717 {
2718  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2719 
2720  if ( geom.isNull() )
2721  return QVariant();
2722 
2723  QgsVertexId vId;
2724  if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
2725  {
2726  return QVariant();
2727  }
2728 
2729  QgsPoint point = geom.constGet()->vertexAt( vId );
2730  return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2731 }
2732 
2733 static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2734 {
2735  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2736 
2737  if ( geom.isNull() )
2738  return QVariant();
2739 
2740  bool ignoreClosing = false;
2741  if ( values.length() > 1 )
2742  {
2743  ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
2744  }
2745 
2746  QgsMultiPoint *mp = new QgsMultiPoint();
2747 
2748  const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
2749  for ( const QgsRingSequence &part : sequence )
2750  {
2751  for ( const QgsPointSequence &ring : part )
2752  {
2753  bool skipLast = false;
2754  if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
2755  {
2756  skipLast = true;
2757  }
2758 
2759  for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
2760  {
2761  mp->addGeometry( ring.at( i ).clone() );
2762  }
2763  }
2764  }
2765 
2766  return QVariant::fromValue( QgsGeometry( mp ) );
2767 }
2768 
2769 static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2770 {
2771  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2772 
2773  if ( geom.isNull() )
2774  return QVariant();
2775 
2776  const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
2777 
2778  //OK, now we have a complete list of segmentized lines from the geometry
2780  for ( QgsLineString *line : linesToProcess )
2781  {
2782  for ( int i = 0; i < line->numPoints() - 1; ++i )
2783  {
2785  segment->setPoints( QgsPointSequence()
2786  << line->pointN( i )
2787  << line->pointN( i + 1 ) );
2788  ml->addGeometry( segment );
2789  }
2790  delete line;
2791  }
2792 
2793  return QVariant::fromValue( QgsGeometry( ml ) );
2794 }
2795 
2796 static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2797 {
2798  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2799 
2800  if ( geom.isNull() )
2801  return QVariant();
2802 
2803  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
2804  if ( !curvePolygon && geom.isMultipart() )
2805  {
2806  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2807  {
2808  if ( collection->numGeometries() == 1 )
2809  {
2810  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
2811  }
2812  }
2813  }
2814 
2815  if ( !curvePolygon )
2816  return QVariant();
2817 
2818  //idx is 1 based
2819  qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
2820 
2821  if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
2822  return QVariant();
2823 
2824  QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
2825  QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
2826  return result;
2827 }
2828 
2829 static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2830 {
2831  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2832 
2833  if ( geom.isNull() )
2834  return QVariant();
2835 
2836  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
2837  if ( !collection )
2838  return QVariant();
2839 
2840  //idx is 1 based
2841  qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
2842 
2843  if ( idx < 0 || idx >= collection->numGeometries() )
2844  return QVariant();
2845 
2846  QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
2847  QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
2848  return result;
2849 }
2850 
2851 static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2852 {
2853  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2854 
2855  if ( geom.isNull() )
2856  return QVariant();
2857 
2858  QgsAbstractGeometry *boundary = geom.constGet()->boundary();
2859  if ( !boundary )
2860  return QVariant();
2861 
2862  return QVariant::fromValue( QgsGeometry( boundary ) );
2863 }
2864 
2865 static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2866 {
2867  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2868 
2869  if ( geom.isNull() )
2870  return QVariant();
2871 
2872  QgsGeometry merged = geom.mergeLines();
2873  if ( merged.isNull() )
2874  return QVariant();
2875 
2876  return QVariant::fromValue( merged );
2877 }
2878 
2879 static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2880 {
2881  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2882 
2883  if ( geom.isNull() )
2884  return QVariant();
2885 
2886  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2887 
2888  QgsGeometry simplified = geom.simplify( tolerance );
2889  if ( simplified.isNull() )
2890  return QVariant();
2891 
2892  return simplified;
2893 }
2894 
2895 static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2896 {
2897  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2898 
2899  if ( geom.isNull() )
2900  return QVariant();
2901 
2902  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2903 
2905 
2906  QgsGeometry simplified = simplifier.simplify( geom );
2907  if ( simplified.isNull() )
2908  return QVariant();
2909 
2910  return simplified;
2911 }
2912 
2913 static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2914 {
2915  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2916 
2917  if ( geom.isNull() )
2918  return QVariant();
2919 
2920  int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
2921  double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
2922  double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
2923  double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
2924 
2925  QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
2926  if ( smoothed.isNull() )
2927  return QVariant();
2928 
2929  return smoothed;
2930 }
2931 
2932 static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2933 {
2934  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2935 
2936  if ( geom.isNull() )
2937  return QVariant();
2938 
2939  const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2940  const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2941  const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
2942 
2943  const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
2944  if ( waved.isNull() )
2945  return QVariant();
2946 
2947  return waved;
2948 }
2949 
2950 static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2951 {
2952  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2953 
2954  if ( geom.isNull() )
2955  return QVariant();
2956 
2957  const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2958  const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2959  const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
2960  const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
2961  const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
2962 
2963  const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
2964  minAmplitude, maxAmplitude, seed );
2965  if ( waved.isNull() )
2966  return QVariant();
2967 
2968  return waved;
2969 }
2970 
2971 static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2972 {
2973  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2974 
2975  if ( geom.isNull() )
2976  return QVariant();
2977 
2978  const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2979  const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2980  const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
2981 
2982  const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
2983  if ( waved.isNull() )
2984  return QVariant();
2985 
2986  return waved;
2987 }
2988 
2989 static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2990 {
2991  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2992 
2993  if ( geom.isNull() )
2994  return QVariant();
2995 
2996  const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2997  const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2998  const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
2999  const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3000  const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3001 
3002  const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3003  minAmplitude, maxAmplitude, seed );
3004  if ( waved.isNull() )
3005  return QVariant();
3006 
3007  return waved;
3008 }
3009 
3010 static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3011 {
3012  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3013 
3014  if ( geom.isNull() )
3015  return QVariant();
3016 
3017  const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3018  const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3019  const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3020 
3021  const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3022  if ( waved.isNull() )
3023  return QVariant();
3024 
3025  return waved;
3026 }
3027 
3028 static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3029 {
3030  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3031 
3032  if ( geom.isNull() )
3033  return QVariant();
3034 
3035  const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3036  const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3037  const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3038  const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3039  const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3040 
3041  const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
3042  minAmplitude, maxAmplitude, seed );
3043  if ( waved.isNull() )
3044  return QVariant();
3045 
3046  return waved;
3047 }
3048 
3049 static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3050 {
3051  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3052 
3053  if ( geom.isNull() )
3054  return QVariant();
3055 
3056  const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
3057  QVector< double > dashPattern;
3058  dashPattern.reserve( pattern.size() );
3059  for ( const QVariant &value : std::as_const( pattern ) )
3060  {
3061  bool ok = false;
3062  double v = value.toDouble( &ok );
3063  if ( ok )
3064  {
3065  dashPattern << v;
3066  }
3067  else
3068  {
3069  parent->setEvalErrorString( QStringLiteral( "Dash pattern must be an array of numbers" ) );
3070  return QgsGeometry();
3071  }
3072  }
3073 
3074  if ( dashPattern.size() % 2 != 0 )
3075  {
3076  parent->setEvalErrorString( QStringLiteral( "Dash pattern must contain an even number of elements" ) );
3077  return QgsGeometry();
3078  }
3079 
3080  const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
3082  if ( startRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3084  else if ( startRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3086  else if ( startRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3088  else if ( startRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3090  else if ( startRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3092  else
3093  {
3094  parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( startRuleString ) );
3095  return QgsGeometry();
3096  }
3097 
3098  const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3100  if ( endRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3102  else if ( endRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3104  else if ( endRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3106  else if ( endRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3108  else if ( endRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3110  else
3111  {
3112  parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( endRuleString ) );
3113  return QgsGeometry();
3114  }
3115 
3116  const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3118  if ( adjustString.compare( QLatin1String( "both" ), Qt::CaseInsensitive ) == 0 )
3120  else if ( adjustString.compare( QLatin1String( "dash" ), Qt::CaseInsensitive ) == 0 )
3122  else if ( adjustString.compare( QLatin1String( "gap" ), Qt::CaseInsensitive ) == 0 )
3124  else
3125  {
3126  parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern size adjustment" ).arg( adjustString ) );
3127  return QgsGeometry();
3128  }
3129 
3130  const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3131 
3132  const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
3133  if ( result.isNull() )
3134  return QVariant();
3135 
3136  return result;
3137 }
3138 
3139 static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3140 {
3141  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3142 
3143  if ( geom.isNull() )
3144  return QVariant();
3145 
3146  const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3147  const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
3148  if ( densified.isNull() )
3149  return QVariant();
3150 
3151  return densified;
3152 }
3153 
3154 static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3155 {
3156  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3157 
3158  if ( geom.isNull() )
3159  return QVariant();
3160 
3161  const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3162  const QgsGeometry densified = geom.densifyByDistance( distance );
3163  if ( densified.isNull() )
3164  return QVariant();
3165 
3166  return densified;
3167 }
3168 
3169 static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3170 {
3171  QVariantList list;
3172  if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
3173  {
3174  list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
3175  }
3176  else
3177  {
3178  list = values;
3179  }
3180 
3181  QVector< QgsGeometry > parts;
3182  parts.reserve( list.size() );
3183  for ( const QVariant &value : std::as_const( list ) )
3184  {
3185  if ( value.canConvert<QgsGeometry>() )
3186  {
3187  parts << value.value<QgsGeometry>();
3188  }
3189  else
3190  {
3191  parent->setEvalErrorString( QStringLiteral( "Cannot convert to geometry" ) );
3192  return QgsGeometry();
3193  }
3194  }
3195 
3196  return QgsGeometry::collectGeometry( parts );
3197 }
3198 
3199 static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3200 {
3201  if ( values.count() < 2 || values.count() > 4 )
3202  {
3203  parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
3204  return QVariant();
3205  }
3206 
3207  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3208  double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3209  double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
3210  double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
3211  switch ( values.count() )
3212  {
3213  case 2:
3214  return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
3215  case 3:
3216  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointZ, x, y, z ) ) );
3217  case 4:
3218  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointZM, x, y, z, m ) ) );
3219  }
3220  return QVariant(); //avoid warning
3221 }
3222 
3223 static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3224 {
3225  double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3226  double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3227  double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3228  return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointM, x, y, 0.0, m ) ) );
3229 }
3230 
3231 static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3232 {
3233  if ( values.empty() )
3234  {
3235  return QVariant();
3236  }
3237 
3238  QVector<QgsPoint> points;
3239  points.reserve( values.count() );
3240 
3241  auto addPoint = [&points]( const QgsGeometry & geom )
3242  {
3243  if ( geom.isNull() )
3244  return;
3245 
3246  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
3247  return;
3248 
3249  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3250  if ( !point )
3251  return;
3252 
3253  points << *point;
3254  };
3255 
3256  for ( const QVariant &value : values )
3257  {
3258  if ( value.type() == QVariant::List )
3259  {
3260  const QVariantList list = value.toList();
3261  for ( const QVariant &v : list )
3262  {
3263  addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
3264  }
3265  }
3266  else
3267  {
3268  addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
3269  }
3270  }
3271 
3272  if ( points.count() < 2 )
3273  return QVariant();
3274 
3275  return QgsGeometry( new QgsLineString( points ) );
3276 }
3277 
3278 static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3279 {
3280  if ( values.count() < 1 )
3281  {
3282  parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
3283  return QVariant();
3284  }
3285 
3286  QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3287 
3288  if ( outerRing.type() == QgsWkbTypes::PolygonGeometry )
3289  return outerRing; // if it's already a polygon we have nothing to do
3290 
3291  if ( outerRing.type() != QgsWkbTypes::LineGeometry || outerRing.isNull() )
3292  return QVariant();
3293 
3294  std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
3295 
3296  const QgsCurve *exteriorRing = qgsgeometry_cast< QgsCurve * >( outerRing.constGet() );
3297  if ( !exteriorRing && outerRing.isMultipart() )
3298  {
3299  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
3300  {
3301  if ( collection->numGeometries() == 1 )
3302  {
3303  exteriorRing = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3304  }
3305  }
3306  }
3307 
3308  if ( !exteriorRing )
3309  return QVariant();
3310 
3311  polygon->setExteriorRing( exteriorRing->segmentize() );
3312 
3313 
3314  for ( int i = 1; i < values.count(); ++i )
3315  {
3316  QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
3317  if ( ringGeom.isNull() )
3318  continue;
3319 
3320  if ( ringGeom.type() != QgsWkbTypes::LineGeometry || ringGeom.isNull() )
3321  continue;
3322 
3323  const QgsCurve *ring = qgsgeometry_cast< QgsCurve * >( ringGeom.constGet() );
3324  if ( !ring && ringGeom.isMultipart() )
3325  {
3326  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
3327  {
3328  if ( collection->numGeometries() == 1 )
3329  {
3330  ring = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3331  }
3332  }
3333  }
3334 
3335  if ( !ring )
3336  continue;
3337 
3338  polygon->addInteriorRing( ring->segmentize() );
3339  }
3340 
3341  return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
3342 }
3343 
3344 static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3345 {
3346  std::unique_ptr<QgsTriangle> tr( new QgsTriangle() );
3347  std::unique_ptr<QgsLineString> lineString( new QgsLineString() );
3348  lineString->clear();
3349 
3350  for ( const QVariant &value : values )
3351  {
3352  QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
3353  if ( geom.isNull() )
3354  return QVariant();
3355 
3356  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
3357  return QVariant();
3358 
3359  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3360  if ( !point && geom.isMultipart() )
3361  {
3362  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3363  {
3364  if ( collection->numGeometries() == 1 )
3365  {
3366  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3367  }
3368  }
3369  }
3370 
3371  if ( !point )
3372  return QVariant();
3373 
3374  lineString->addVertex( *point );
3375  }
3376 
3377  tr->setExteriorRing( lineString.release() );
3378 
3379  return QVariant::fromValue( QgsGeometry( tr.release() ) );
3380 }
3381 
3382 static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3383 {
3384  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3385  if ( geom.isNull() )
3386  return QVariant();
3387 
3388  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
3389  return QVariant();
3390 
3391  double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3392  int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3393 
3394  if ( segment < 3 )
3395  {
3396  parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3397  return QVariant();
3398  }
3399  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3400  if ( !point && geom.isMultipart() )
3401  {
3402  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3403  {
3404  if ( collection->numGeometries() == 1 )
3405  {
3406  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3407  }
3408  }
3409  }
3410  if ( !point )
3411  return QVariant();
3412 
3413  QgsCircle circ( *point, radius );
3414  return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3415 }
3416 
3417 static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3418 {
3419  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3420  if ( geom.isNull() )
3421  return QVariant();
3422 
3423  if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
3424  return QVariant();
3425 
3426  double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3427  double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3428  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3429  int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
3430  if ( segment < 3 )
3431  {
3432  parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3433  return QVariant();
3434  }
3435  const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3436  if ( !point && geom.isMultipart() )
3437  {
3438  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3439  {
3440  if ( collection->numGeometries() == 1 )
3441  {
3442  point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3443  }
3444  }
3445  }
3446  if ( !point )
3447  return QVariant();
3448 
3449  QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
3450  return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3451 }
3452 
3453 static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3454 {
3455 
3456  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3457  if ( pt1.isNull() )
3458  return QVariant();
3459 
3460  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3461  return QVariant();
3462 
3463  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3464  if ( pt2.isNull() )
3465  return QVariant();
3466 
3467  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3468  return QVariant();
3469 
3470  unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
3471  if ( nbEdges < 3 )
3472  {
3473  parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
3474  return QVariant();
3475  }
3476 
3477  QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3479  {
3480  parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
3481  return QVariant();
3482  }
3483 
3484  const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
3485  if ( !center && pt1.isMultipart() )
3486  {
3487  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
3488  {
3489  if ( collection->numGeometries() == 1 )
3490  {
3491  center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3492  }
3493  }
3494  }
3495  if ( !center )
3496  return QVariant();
3497 
3498  const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
3499  if ( !corner && pt2.isMultipart() )
3500  {
3501  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
3502  {
3503  if ( collection->numGeometries() == 1 )
3504  {
3505  corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3506  }
3507  }
3508  }
3509  if ( !corner )
3510  return QVariant();
3511 
3512  QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
3513 
3514  return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
3515 
3516 }
3517 
3518 static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3519 {
3520  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3521  if ( pt1.isNull() )
3522  return QVariant();
3523  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3524  return QVariant();
3525 
3526  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3527  if ( pt2.isNull() )
3528  return QVariant();
3529  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3530  return QVariant();
3531 
3532  const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
3533  const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
3534  QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
3535 
3536  return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
3537 }
3538 
3539 static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3540 {
3541  QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3542  if ( pt1.isNull() )
3543  return QVariant();
3544  if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3545  return QVariant();
3546 
3547  QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3548  if ( pt2.isNull() )
3549  return QVariant();
3550  if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3551  return QVariant();
3552 
3553  QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
3554  if ( pt3.isNull() )
3555  return QVariant();
3556  if ( pt3.type() != QgsWkbTypes::PointGeometry || pt3.isMultipart() )
3557  return QVariant();
3558 
3559  QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3560  if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
3561  {
3562  parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
3563  return QVariant();
3564  }
3565  const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
3566  const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
3567  const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
3568  QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
3569  return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
3570 }
3571 
3572 static QVariant pointAt( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) // helper function
3573 {
3574  FEAT_FROM_CONTEXT( context, f )
3575  int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
3576  QgsGeometry g = f.geometry();
3577  if ( g.isNull() )
3578  return QVariant();
3579 
3580  if ( idx < 0 )
3581  {
3582  idx += g.constGet()->nCoordinates();
3583  }
3584  if ( idx < 0 || idx >= g.constGet()->nCoordinates() )
3585  {
3586  parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
3587  return QVariant();
3588  }
3589 
3590  QgsPointXY p = g.vertexAt( idx );
3591  return QVariant( QPointF( p.x(), p.y() ) );
3592 }
3593 
3594 static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )
3595 {
3596  QVariant v = pointAt( values, f, parent );
3597  if ( v.type() == QVariant::PointF )
3598  return QVariant( v.toPointF().x() );
3599  else
3600  return QVariant();
3601 }
3602 static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )
3603 {
3604  QVariant v = pointAt( values, f, parent );
3605  if ( v.type() == QVariant::PointF )
3606  return QVariant( v.toPointF().y() );
3607  else
3608  return QVariant();
3609 }
3610 static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3611 {
3612  if ( !context )
3613  return QVariant();
3614 
3615  // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
3616  if ( context->hasGeometry() )
3617  return context->geometry();
3618  else
3619  {
3620  FEAT_FROM_CONTEXT( context, f )
3621  QgsGeometry geom = f.geometry();
3622  if ( !geom.isNull() )
3623  return QVariant::fromValue( geom );
3624  else
3625  return QVariant();
3626  }
3627 }
3628 
3629 static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3630 {
3631  QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3632  QgsGeometry geom = QgsGeometry::fromWkt( wkt );
3633  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3634  return result;
3635 }
3636 
3637 static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3638 {
3639  const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
3640  if ( wkb.isNull() )
3641  return QVariant();
3642 
3643  QgsGeometry geom;
3644  geom.fromWkb( wkb );
3645  return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3646 }
3647 
3648 static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3649 {
3650  QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3651  QgsOgcUtils::Context ogcContext;
3652  if ( context )
3653  {
3654  QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
3655  if ( mapLayerPtr )
3656  {
3657  ogcContext.layer = mapLayerPtr.data();
3658  ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
3659  }
3660  }
3661  QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
3662  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3663  return result;
3664 }
3665 
3666 static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3667 {
3668  FEAT_FROM_CONTEXT( context, f )
3670  QgsDistanceArea *calc = parent->geomCalculator();
3671  if ( calc )
3672  {
3673  double area = calc->measureArea( f.geometry() );
3674  area = calc->convertAreaMeasurement( area, parent->areaUnits() );
3675  return QVariant( area );
3676  }
3677  else
3678  {
3679  return QVariant( f.geometry().area() );
3680  }
3681 }
3682 
3683 static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3684 {
3685  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3686 
3687  if ( geom.type() != QgsWkbTypes::PolygonGeometry )
3688  return QVariant();
3689 
3690  return QVariant( geom.area() );
3691 }
3692 
3693 static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3694 {
3695  FEAT_FROM_CONTEXT( context, f )
3697  QgsDistanceArea *calc = parent->geomCalculator();
3698  if ( calc )
3699  {
3700  double len = calc->measureLength( f.geometry() );
3701  len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
3702  return QVariant( len );
3703  }
3704  else
3705  {
3706  return QVariant( f.geometry().length() );
3707  }
3708 }
3709 
3710 static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3711 {
3712  FEAT_FROM_CONTEXT( context, f )
3714  QgsDistanceArea *calc = parent->geomCalculator();
3715  if ( calc )
3716  {
3717  double len = calc->measurePerimeter( f.geometry() );
3718  len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
3719  return QVariant( len );
3720  }
3721  else
3722  {
3723  return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
3724  }
3725 }
3726 
3727 static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3728 {
3729  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3730 
3731  if ( geom.type() != QgsWkbTypes::PolygonGeometry )
3732  return QVariant();
3733 
3734  //length for polygons = perimeter
3735  return QVariant( geom.length() );
3736 }
3737 
3738 static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3739 {
3740  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3741  return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
3742 }
3743 
3744 static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3745 {
3746  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3747  if ( geom.isNull() )
3748  return QVariant();
3749 
3750  return QVariant( geom.constGet()->partCount() );
3751 }
3752 
3753 static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3754 {
3755  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3756  if ( geom.isNull() )
3757  return QVariant();
3758 
3759  return QVariant( geom.isMultipart() );
3760 }
3761 
3762 static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3763 {
3764  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3765 
3766  if ( geom.isNull() )
3767  return QVariant();
3768 
3769  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3770  if ( curvePolygon )
3771  return QVariant( curvePolygon->numInteriorRings() );
3772 
3773  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3774  if ( collection )
3775  {
3776  //find first CurvePolygon in collection
3777  for ( int i = 0; i < collection->numGeometries(); ++i )
3778  {
3779  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
3780  if ( !curvePolygon )
3781  continue;
3782 
3783  return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
3784  }
3785  }
3786 
3787  return QVariant();
3788 }
3789 
3790 static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3791 {
3792  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3793 
3794  if ( geom.isNull() )
3795  return QVariant();
3796 
3797  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3798  if ( curvePolygon )
3799  return QVariant( curvePolygon->ringCount() );
3800 
3801  bool foundPoly = false;
3802  int ringCount = 0;
3803  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3804  if ( collection )
3805  {
3806  //find CurvePolygons in collection
3807  for ( int i = 0; i < collection->numGeometries(); ++i )
3808  {
3809  curvePolygon = qgsgeometry_cast< QgsCurvePolygon *>( collection->geometryN( i ) );
3810  if ( !curvePolygon )
3811  continue;
3812 
3813  foundPoly = true;
3814  ringCount += curvePolygon->ringCount();
3815  }
3816  }
3817 
3818  if ( !foundPoly )
3819  return QVariant();
3820 
3821  return QVariant( ringCount );
3822 }
3823 
3824 static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3825 {
3826  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3827  QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
3828  QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
3829  return result;
3830 }
3831 
3832 static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3833 {
3834  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3835  return QVariant::fromValue( geom.boundingBox().width() );
3836 }
3837 
3838 static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3839 {
3840  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3841  return QVariant::fromValue( geom.boundingBox().height() );
3842 }
3843 
3844 static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3845 {
3846  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3847  if ( geom.isNull() )
3848  return QVariant();
3849 
3850  return QgsWkbTypes::geometryDisplayString( geom.type() );
3851 }
3852 
3853 static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3854 {
3855  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3856  return QVariant::fromValue( geom.boundingBox().xMinimum() );
3857 }
3858 
3859 static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3860 {
3861  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3862  return QVariant::fromValue( geom.boundingBox().xMaximum() );
3863 }
3864 
3865 static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3866 {
3867  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3868  return QVariant::fromValue( geom.boundingBox().yMinimum() );
3869 }
3870 
3871 static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3872 {
3873  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3874  return QVariant::fromValue( geom.boundingBox().yMaximum() );
3875 }
3876 
3877 static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3878 {
3879  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3880 
3881  if ( geom.isNull() || geom.isEmpty( ) )
3882  return QVariant();
3883 
3884  if ( !geom.constGet()->is3D() )
3885  return QVariant();
3886 
3887  double max = std::numeric_limits< double >::lowest();
3888 
3889  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3890  {
3891  double z = ( *it ).z();
3892 
3893  if ( max < z )
3894  max = z;
3895  }
3896 
3897  if ( max == std::numeric_limits< double >::lowest() )
3898  return QVariant( QVariant::Double );
3899 
3900  return QVariant( max );
3901 }
3902 
3903 static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3904 {
3905  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3906 
3907  if ( geom.isNull() || geom.isEmpty() )
3908  return QVariant();
3909 
3910  if ( !geom.constGet()->is3D() )
3911  return QVariant();
3912 
3913  double min = std::numeric_limits< double >::max();
3914 
3915  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3916  {
3917  double z = ( *it ).z();
3918 
3919  if ( z < min )
3920  min = z;
3921  }
3922 
3923  if ( min == std::numeric_limits< double >::max() )
3924  return QVariant( QVariant::Double );
3925 
3926  return QVariant( min );
3927 }
3928 
3929 static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3930 {
3931  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3932 
3933  if ( geom.isNull() || geom.isEmpty() )
3934  return QVariant();
3935 
3936  if ( !geom.constGet()->isMeasure() )
3937  return QVariant();
3938 
3939  double min = std::numeric_limits< double >::max();
3940 
3941  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3942  {
3943  double m = ( *it ).m();
3944 
3945  if ( m < min )
3946  min = m;
3947  }
3948 
3949  if ( min == std::numeric_limits< double >::max() )
3950  return QVariant( QVariant::Double );
3951 
3952  return QVariant( min );
3953 }
3954 
3955 static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3956 {
3957  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3958 
3959  if ( geom.isNull() || geom.isEmpty() )
3960  return QVariant();
3961 
3962  if ( !geom.constGet()->isMeasure() )
3963  return QVariant();
3964 
3965  double max = std::numeric_limits< double >::lowest();
3966 
3967  for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3968  {
3969  double m = ( *it ).m();
3970 
3971  if ( max < m )
3972  max = m;
3973  }
3974 
3975  if ( max == std::numeric_limits< double >::lowest() )
3976  return QVariant( QVariant::Double );
3977 
3978  return QVariant( max );
3979 }
3980 
3981 static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3982 {
3983  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3984  const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom.constGet() );
3985  if ( !curve )
3986  {
3987  parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
3988  return QVariant();
3989  }
3990 
3991  return QVariant( curve->sinuosity() );
3992 }
3993 
3994 static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3995 {
3996  const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3997  const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
3998  if ( !curve )
3999  {
4000  parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
4001  return QVariant();
4002  }
4003 
4004  return QVariant( curve->straightDistance2d() );
4005 }
4006 
4007 static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4008 {
4009  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4010  const QgsCurvePolygon *poly = geom.constGet() ? qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4011 
4012  if ( !poly )
4013  {
4014  parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
4015  return QVariant();
4016  }
4017 
4018  return QVariant( poly->roundness() );
4019 }
4020 
4021 
4022 
4023 static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4024 {
4025  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4026  if ( geom.isNull() )
4027  return QVariant();
4028 
4029  std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
4030  flipped->swapXy();
4031  return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
4032 }
4033 
4034 static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4035 {
4036  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4037  if ( fGeom.isNull() )
4038  return QVariant();
4039 
4040  const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
4041  if ( !curve && fGeom.isMultipart() )
4042  {
4043  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4044  {
4045  if ( collection->numGeometries() == 1 )
4046  {
4047  curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4048  }
4049  }
4050  }
4051 
4052  if ( !curve )
4053  return QVariant();
4054 
4055  return QVariant::fromValue( curve->isClosed() );
4056 }
4057 
4058 static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4059 {
4060  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4061 
4062  if ( geom.isNull() )
4063  return QVariant();
4064 
4065  QVariant result;
4066  if ( !geom.isMultipart() )
4067  {
4068  const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
4069 
4070  if ( !line )
4071  return QVariant();
4072 
4073  std::unique_ptr< QgsLineString > closedLine( line->clone() );
4074  closedLine->close();
4075 
4076  result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
4077  }
4078  else
4079  {
4080  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
4081 
4082  std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
4083 
4084  for ( int i = 0; i < collection->numGeometries(); ++i )
4085  {
4086  if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
4087  {
4088  std::unique_ptr< QgsLineString > closedLine( line->clone() );
4089  closedLine->close();
4090 
4091  closed->addGeometry( closedLine.release() );
4092  }
4093  }
4094  result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
4095  }
4096 
4097  return result;
4098 }
4099 
4100 static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4101 {
4102  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4103  if ( fGeom.isNull() )
4104  return QVariant();
4105 
4106  return QVariant::fromValue( fGeom.isEmpty() );
4107 }
4108 
4109 static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4110 {
4111  if ( values.at( 0 ).isNull() )
4112  return QVariant::fromValue( true );
4113 
4114  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4115  return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
4116 }
4117 
4118 static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4119 {
4120  if ( values.length() < 2 || values.length() > 3 )
4121  return QVariant();
4122 
4123  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4124  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4125 
4126  if ( fGeom.isNull() || sGeom.isNull() )
4127  return QVariant();
4128 
4129  std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
4130 
4131  if ( values.length() == 2 )
4132  {
4133  //two geometry arguments, return relation
4134  QString result = engine->relate( sGeom.constGet() );
4135  return QVariant::fromValue( result );
4136  }
4137  else
4138  {
4139  //three arguments, test pattern
4140  QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4141  bool result = engine->relatePattern( sGeom.constGet(), pattern );
4142  return QVariant::fromValue( result );
4143  }
4144 }
4145 
4146 static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4147 {
4148  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4149  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4150  return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
4151 }
4152 static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4153 {
4154  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4155  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4156  return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
4157 }
4158 static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4159 {
4160  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4161  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4162  return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
4163 }
4164 static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4165 {
4166  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4167  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4168  return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
4169 }
4170 static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4171 {
4172  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4173  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4174  return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
4175 }
4176 static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4177 {
4178  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4179  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4180  return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
4181 }
4182 static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4183 {
4184  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4185  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4186  return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
4187 }
4188 static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4189 {
4190  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4191  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4192  return fGeom.within( sGeom ) ? TVL_True : TVL_False;
4193 }
4194 
4195 static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4196 {
4197  const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4198  const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4199  const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4200  const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4201  const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4202  const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4203 
4204  Qgis::EndCapStyle capStyle = Qgis::EndCapStyle::Round;
4205  if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
4206  capStyle = Qgis::EndCapStyle::Flat;
4207  else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
4208  capStyle = Qgis::EndCapStyle::Square;
4209 
4210  Qgis::JoinStyle joinStyle = Qgis::JoinStyle::Round;
4211  if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
4212  joinStyle = Qgis::JoinStyle::Miter;
4213  else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
4214  joinStyle = Qgis::JoinStyle::Bevel;
4215 
4216  QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
4217  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4218  return result;
4219 }
4220 
4221 static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4222 {
4223  const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4224  const QgsGeometry reoriented = fGeom.forceRHR();
4225  return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4226 }
4227 
4228 static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4229 {
4230  const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4231  const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
4232  return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4233 }
4234 
4235 static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4236 {
4237  const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4238  const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
4239  return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4240 }
4241 
4242 static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4243 {
4244  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4245  const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
4246  if ( !pt && fGeom.isMultipart() )
4247  {
4248  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4249  {
4250  if ( collection->numGeometries() == 1 )
4251  {
4252  pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4253  }
4254  }
4255  }
4256 
4257  if ( !pt )
4258  {
4259  parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
4260  return QVariant();
4261  }
4262 
4263  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4264  double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4265  double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4266  double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4267 
4268  QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
4269  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4270  return result;
4271 }
4272 
4273 static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4274 {
4275  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4276  if ( fGeom.type() != QgsWkbTypes::LineGeometry )
4277  {
4278  parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
4279  return QVariant();
4280  }
4281 
4282  double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4283  double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4284  int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4285 
4286  QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
4287  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4288  return result;
4289 }
4290 
4291 static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4292 {
4293  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4294  if ( fGeom.type() != QgsWkbTypes::LineGeometry )
4295  {
4296  parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
4297  return QVariant();
4298  }
4299 
4300  int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
4301 
4302  QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
4303  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4304  return result;
4305 }
4306 
4307 static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4308 {
4309  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4310  double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4311  int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4312  const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4313  if ( joinInt < 1 || joinInt > 3 )
4314  return QVariant();
4315  const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4316 
4317  double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4318 
4319  QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
4320  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4321  return result;
4322 }
4323 
4324 static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4325 {
4326  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4327  double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4328  int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4329 
4330  const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4331  if ( joinInt < 1 || joinInt > 3 )
4332  return QVariant();
4333  const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4334 
4335  double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4336 
4337  QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
4338  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4339  return result;
4340 }
4341 
4342 static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4343 {
4344  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4345  double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4346  double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4347 
4348  QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
4349  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4350  return result;
4351 }
4352 
4353 static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4354 {
4355  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4356  double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4357  double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4358  fGeom.translate( dx, dy );
4359  return QVariant::fromValue( fGeom );
4360 }
4361 
4362 static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4363 {
4364  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4365  const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4366  const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
4367  : QgsGeometry();
4368  const bool perPart = values.value( 3 ).toBool();
4369 
4370  if ( center.isNull() && perPart && fGeom.isMultipart() )
4371  {
4372  // no explicit center, rotating per part
4373  // (note that we only do this branch for multipart geometries -- for singlepart geometries
4374  // the result is equivalent to setting perPart as false anyway)
4375  std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
4376  for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
4377  {
4378  const QgsPointXY partCenter = ( *it )->boundingBox().center();
4379  QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
4380  t.rotate( -rotation );
4381  t.translate( -partCenter.x(), -partCenter.y() );
4382  ( *it )->transform( t );
4383  }
4384  return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
4385  }
4386  else
4387  {
4388  QgsPointXY pt;
4389  if ( center.isEmpty() )
4390  {
4391  // if center wasn't specified, use bounding box centroid
4392  pt = fGeom.boundingBox().center();
4393  }
4395  {
4396  parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
4397  return QVariant();
4398  }
4399  else
4400  {
4401  pt = QgsPointXY( *qgsgeometry_cast< const QgsPoint * >( center.constGet()->simplifiedTypeRef() ) );
4402  }
4403 
4404  fGeom.rotate( rotation, pt );
4405  return QVariant::fromValue( fGeom );
4406  }
4407 }
4408 
4409 static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4410 {
4411  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4412  const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4413  const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4414  const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
4415  : QgsGeometry();
4416 
4417  QgsPointXY pt;
4418  if ( center.isNull() )
4419  {
4420  // if center wasn't specified, use bounding box centroid
4421  pt = fGeom.boundingBox().center();
4422  }
4424  {
4425  parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
4426  return QVariant();
4427  }
4428  else
4429  {
4430  pt = center.asPoint();
4431  }
4432 
4433  QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
4434  t.scale( xScale, yScale );
4435  t.translate( -pt.x(), -pt.y() );
4436  fGeom.transform( t );
4437  return QVariant::fromValue( fGeom );
4438 }
4439 
4440 static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4441 {
4442  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4443  if ( fGeom.isNull() )
4444  {
4445  return QVariant();
4446  }
4447 
4448  const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4449  const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4450 
4451  const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4452 
4453  const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4454  const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4455 
4456  const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
4457  const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
4458  const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
4459  const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
4460 
4461  if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
4462  {
4463  fGeom.get()->addZValue( 0 );
4464  }
4465  if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
4466  {
4467  fGeom.get()->addMValue( 0 );
4468  }
4469 
4470  QTransform transform;
4471  transform.translate( deltaX, deltaY );
4472  transform.rotate( rotationZ );
4473  transform.scale( scaleX, scaleY );
4474  fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
4475 
4476  return QVariant::fromValue( fGeom );
4477 }
4478 
4479 
4480 static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4481 {
4482  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4483  QgsGeometry geom = fGeom.centroid();
4484  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4485  return result;
4486 }
4487 static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4488 {
4489  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4490  QgsGeometry geom = fGeom.pointOnSurface();
4491  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4492  return result;
4493 }
4494 
4495 static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4496 {
4497  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4498  double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4499  QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
4500  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4501  return result;
4502 }
4503 
4504 static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4505 {
4506  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4507  QgsGeometry geom = fGeom.convexHull();
4508  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4509  return result;
4510 }
4511 
4512 
4513 static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4514 {
4515  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4516  int segments = 36;
4517  if ( values.length() == 2 )
4518  segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4519  if ( segments < 0 )
4520  {
4521  parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
4522  return QVariant();
4523  }
4524 
4525  QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
4526  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4527  return result;
4528 }
4529 
4530 static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4531 {
4532  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4533  QgsGeometry geom = fGeom.orientedMinimumBoundingBox( );
4534  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4535  return result;
4536 }
4537 
4538 static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4539 {
4540  const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4541 
4542  // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
4543  // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
4544  // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
4545 
4546  double area, angle, width, height;
4547  const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
4548 
4549  if ( geom.isNull() )
4550  {
4551  parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
4552  return QVariant();
4553  }
4554  return angle;
4555 }
4556 
4557 static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4558 {
4559  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4560  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4561  QgsGeometry geom = fGeom.difference( sGeom );
4562  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4563  return result;
4564 }
4565 
4566 static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4567 {
4568  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4569  if ( fGeom.isNull() )
4570  return QVariant();
4571 
4572  QVariant result;
4573  if ( !fGeom.isMultipart() )
4574  {
4575  const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
4576  if ( !curve )
4577  return QVariant();
4578 
4579  QgsCurve *reversed = curve->reversed();
4580  result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
4581  }
4582  else
4583  {
4584  const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
4585  std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
4586  for ( int i = 0; i < collection->numGeometries(); ++i )
4587  {
4588  if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
4589  {
4590  reversed->addGeometry( curve->reversed() );
4591  }
4592  else
4593  {
4594  reversed->addGeometry( collection->geometryN( i )->clone() );
4595  }
4596  }
4597  result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
4598  }
4599  return result;
4600 }
4601 
4602 static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4603 {
4604  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4605  if ( fGeom.isNull() )
4606  return QVariant();
4607 
4608  const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
4609  if ( !curvePolygon && fGeom.isMultipart() )
4610  {
4611  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4612  {
4613  if ( collection->numGeometries() == 1 )
4614  {
4615  curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
4616  }
4617  }
4618  }
4619 
4620  if ( !curvePolygon || !curvePolygon->exteriorRing() )
4621  return QVariant();
4622 
4623  QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
4624  QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
4625  return result;
4626 }
4627 
4628 static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4629 {
4630  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4631  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4632  return QVariant( fGeom.distance( sGeom ) );
4633 }
4634 
4635 static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4636 {
4637  QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4638  QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4639 
4640  double res = -1;
4641  if ( values.length() == 3 && values.at( 2 ).isValid() )
4642  {
4643  double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4644  densify = std::clamp( densify, 0.0, 1.0 );
4645  res = g1.hausdorffDistanceDensify( g2, densify );
4646  }
4647  else
4648  {
4649  res = g1.hausdorffDistance( g2 );
4650  }
4651 
4652  return res > -1 ? QVariant( res ) : QVariant();
4653 }
4654 
4655 static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4656 {
4657  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4658  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4659  QgsGeometry geom = fGeom.intersection( sGeom );
4660  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4661  return result;
4662 }
4663 static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4664 {
4665  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4666  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4667  QgsGeometry geom = fGeom.symDifference( sGeom );
4668  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4669  return result;
4670 }
4671 static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4672 {
4673  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4674  QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4675  QgsGeometry geom = fGeom.combine( sGeom );
4676  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4677  return result;
4678 }
4679 
4680 static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4681 {
4682  if ( values.length() < 1 || values.length() > 2 )
4683  return QVariant();
4684 
4685  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4686  int prec = 8;
4687  if ( values.length() == 2 )
4688  prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4689  QString wkt = fGeom.asWkt( prec );
4690  return QVariant( wkt );
4691 }
4692 
4693 static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4694 {
4695  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4696  return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
4697 }
4698 
4699 static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4700 {
4701  if ( values.length() != 2 )
4702  {
4703  parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
4704  return QVariant();
4705  }
4706 
4707  QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4708  QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4709 
4710  const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
4711  if ( !pt1 && fGeom1.isMultipart() )
4712  {
4713  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
4714  {
4715  if ( collection->numGeometries() == 1 )
4716  {
4717  pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4718  }
4719  }
4720  }
4721 
4722  const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
4723  if ( !pt2 && fGeom2.isMultipart() )
4724  {
4725  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
4726  {
4727  if ( collection->numGeometries() == 1 )
4728  {
4729  pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4730  }
4731  }
4732  }
4733 
4734  if ( !pt1 || !pt2 )
4735  {
4736  parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
4737  return QVariant();
4738  }
4739 
4740  // Code from PostGIS
4741  if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
4742  {
4743  if ( pt1->y() < pt2->y() )
4744  return 0.0;
4745  else if ( pt1->y() > pt2->y() )
4746  return M_PI;
4747  else
4748  return 0;
4749  }
4750 
4751  if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
4752  {
4753  if ( pt1->x() < pt2->x() )
4754  return M_PI_2;
4755  else if ( pt1->x() > pt2->x() )
4756  return M_PI + ( M_PI_2 );
4757  else
4758  return 0;
4759  }
4760 
4761  if ( pt1->x() < pt2->x() )
4762  {
4763  if ( pt1->y() < pt2->y() )
4764  {
4765  return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
4766  }
4767  else /* ( pt1->y() > pt2->y() ) - equality case handled above */
4768  {
4769  return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
4770  + ( M_PI_2 );
4771  }
4772  }
4773 
4774  else /* ( pt1->x() > pt2->x() ) - equality case handled above */
4775  {
4776  if ( pt1->y() > pt2->y() )
4777  {
4778  return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
4779  + M_PI;
4780  }
4781  else /* ( pt1->y() < pt2->y() ) - equality case handled above */
4782  {
4783  return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
4784  + ( M_PI + ( M_PI_2 ) );
4785  }
4786  }
4787 }
4788 
4789 static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4790 {
4791  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4792 
4793  if ( ! geom.constGet() || QgsWkbTypes::flatType( geom.constGet()->simplifiedTypeRef( )->wkbType() ) != QgsWkbTypes::Type::Point )
4794  {
4795  parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
4796  return QVariant();
4797  }
4798 
4799  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4800  double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4801  double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4802 
4803  const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
4804  QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
4805 
4806  return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
4807 }
4808 
4809 static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4810 {
4811  QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4812  QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4813 
4814  const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
4815  if ( !pt1 && fGeom1.isMultipart() )
4816  {
4817  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
4818  {
4819  if ( collection->numGeometries() == 1 )
4820  {
4821  pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4822  }
4823  }
4824  }
4825  const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
4826  if ( !pt2 && fGeom2.isMultipart() )
4827  {
4828  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
4829  {
4830  if ( collection->numGeometries() == 1 )
4831  {
4832  pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4833  }
4834  }
4835  }
4836 
4837  if ( ( fGeom1.type() != QgsWkbTypes::PointGeometry ) || ( fGeom2.type() != QgsWkbTypes::PointGeometry ) ||
4838  !pt1 || !pt2 )
4839  {
4840  parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
4841  return QVariant();
4842  }
4843 
4844  return pt1->inclination( *pt2 );
4845 
4846 }
4847 
4848 static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4849 {
4850  if ( values.length() != 3 )
4851  return QVariant();
4852 
4853  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4854  double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4855  double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4856 
4857  QgsGeometry geom = fGeom.extrude( x, y );
4858 
4859  QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
4860  return result;
4861 }
4862 
4863 static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
4864 {
4865  if ( values.length() < 2 )
4866  return QVariant();
4867 
4868  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4869 
4870  if ( !fGeom.isMultipart() )
4871  return values.at( 0 );
4872 
4873  QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4874  QVariant cachedExpression;
4875  if ( ctx )
4876  cachedExpression = ctx->cachedValue( expString );
4877  QgsExpression expression;
4878 
4879  if ( cachedExpression.isValid() )
4880  {
4881  expression = cachedExpression.value<QgsExpression>();
4882  }
4883  else
4884  expression = QgsExpression( expString );
4885 
4886  bool asc = values.value( 2 ).toBool();
4887 
4888  QgsExpressionContext *unconstedContext = nullptr;
4889  QgsFeature f;
4890  if ( ctx )
4891  {
4892  // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
4893  // so no reason to worry
4894  unconstedContext = const_cast<QgsExpressionContext *>( ctx );
4895  f = ctx->feature();
4896  }
4897  else
4898  {
4899  // If there's no context provided, create a fake one
4900  unconstedContext = new QgsExpressionContext();
4901  }
4902 
4903  const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
4904  Q_ASSERT( collection ); // Should have failed the multipart check above
4905 
4907  orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
4908  QgsExpressionSorter sorter( orderBy );
4909 
4910  QList<QgsFeature> partFeatures;
4911  partFeatures.reserve( collection->partCount() );
4912  for ( int i = 0; i < collection->partCount(); ++i )
4913  {
4914  f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
4915  partFeatures << f;
4916  }
4917 
4918  sorter.sortFeatures( partFeatures, unconstedContext );
4919 
4920  QgsGeometryCollection *orderedGeom = qgsgeometry_cast<QgsGeometryCollection *>( fGeom.constGet()->clone() );
4921 
4922  Q_ASSERT( orderedGeom );
4923 
4924  while ( orderedGeom->partCount() )
4925  orderedGeom->removeGeometry( 0 );
4926 
4927  for ( const QgsFeature &feature : std::as_const( partFeatures ) )
4928  {
4929  orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
4930  }
4931 
4932  QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
4933 
4934  if ( !ctx )
4935  delete unconstedContext;
4936 
4937  return result;
4938 }
4939 
4940 static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4941 {
4942  QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4943  QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4944 
4945  QgsGeometry geom = fromGeom.nearestPoint( toGeom );
4946 
4947  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4948  return result;
4949 }
4950 
4951 static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4952 {
4953  QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4954  QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4955 
4956  QgsGeometry geom = fromGeom.shortestLine( toGeom );
4957 
4958  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4959  return result;
4960 }
4961 
4962 static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4963 {
4964  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4965  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4966 
4967  QgsGeometry geom = lineGeom.interpolate( distance );
4968 
4969  QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4970  return result;
4971 }
4972 
4973 static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4974 {
4975  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4976  if ( lineGeom.type() != QgsWkbTypes::LineGeometry )
4977  {
4978  parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
4979  return QVariant();
4980  }
4981 
4982  const QgsCurve *curve = nullptr;
4983  if ( !lineGeom.isMultipart() )
4984  curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
4985  else
4986  {
4987  if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
4988  {
4989  if ( collection->numGeometries() > 0 )
4990  {
4991  curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4992  }
4993  }
4994  }
4995  if ( !curve )
4996  return QVariant();
4997 
4998  double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4999  double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5000 
5001  std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
5002  QgsGeometry result( std::move( substring ) );
5003  return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
5004 }
5005 
5006 static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5007 {
5008  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5009  double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5010 
5011  return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
5012 }
5013 
5014 static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5015 {
5016  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5017  int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5018  if ( vertex < 0 )
5019  {
5020  //negative idx
5021  int count = geom.constGet()->nCoordinates();
5022  vertex = count + vertex;
5023  }
5024 
5025  return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
5026 }
5027 
5028 static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5029 {
5030  QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5031  int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5032  if ( vertex < 0 )
5033  {
5034  //negative idx
5035  int count = geom.constGet()->nCoordinates();
5036  vertex = count + vertex;
5037  }
5038 
5039  return geom.distanceToVertex( vertex );
5040 }
5041 
5042 static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5043 {
5044  QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5045  QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5046 
5047  double distance = lineGeom.lineLocatePoint( pointGeom );
5048 
5049  return distance >= 0 ? distance : QVariant();
5050 }
5051 
5052 static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5053 {
5054  if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
5055  {
5056  double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5057  return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5058  }
5059 
5060  if ( values.length() >= 1 )
5061  {
5062  double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5063  return QVariant( qlonglong( std::round( number ) ) );
5064  }
5065 
5066  return QVariant();
5067 }
5068 
5069 static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5070 {
5071  Q_UNUSED( values )
5072  Q_UNUSED( parent )
5073  return M_PI;
5074 }
5075 
5076 static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5077 {
5078  const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5079  const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5080  const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5081  if ( places < 0 )
5082  {
5083  parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
5084  return QVariant();
5085  }
5086 
5087  QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5088  locale.setNumberOptions( locale.numberOptions() &= ~QLocale::NumberOption::OmitGroupSeparator );
5089  return locale.toString( value, 'f', places );
5090 }
5091 
5092 static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5093 {
5094  const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
5095  const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5096  const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5097 
5098  QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5099  return locale.toString( datetime, format );
5100 }
5101 
5102 static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5103 {
5104  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5105  int avg = ( color.red() + color.green() + color.blue() ) / 3;
5106  int alpha = color.alpha();
5107 
5108  color.setRgb( avg, avg, avg, alpha );
5109 
5110  return QgsSymbolLayerUtils::encodeColor( color );
5111 }
5112 
5113 static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5114 {
5115  QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5116  QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
5117  double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5118  if ( ratio > 1 )
5119  {
5120  ratio = 1;
5121  }
5122  else if ( ratio < 0 )
5123  {
5124  ratio = 0;
5125  }
5126 
5127  int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
5128  int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
5129  int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
5130  int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
5131 
5132  QColor newColor( red, green, blue, alpha );
5133 
5134  return QgsSymbolLayerUtils::encodeColor( newColor );
5135 }
5136 
5137 static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5138 {
5139  int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
5140  int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5141  int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5142  QColor color = QColor( red, green, blue );
5143  if ( ! color.isValid() )
5144  {
5145  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
5146  color = QColor( 0, 0, 0 );
5147  }
5148 
5149  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5150 }
5151 
5152 static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5153 {
5154  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
5155  QVariant value = node->eval( parent, context );
5156  if ( parent->hasEvalError() )
5157  {
5158  parent->setEvalErrorString( QString() );
5159  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
5161  value = node->eval( parent, context );
5163  }
5164  return value;
5165 }
5166 
5167 static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5168 {
5169  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
5171  QVariant value = node->eval( parent, context );
5173  if ( value.toBool() )
5174  {
5175  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
5177  value = node->eval( parent, context );
5179  }
5180  else
5181  {
5182  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
5184  value = node->eval( parent, context );
5186  }
5187  return value;
5188 }
5189 
5190 static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5191 {
5192  int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
5193  int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5194  int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5195  int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
5196  QColor color = QColor( red, green, blue, alpha );
5197  if ( ! color.isValid() )
5198  {
5199  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
5200  color = QColor( 0, 0, 0 );
5201  }
5202  return QgsSymbolLayerUtils::encodeColor( color );
5203 }
5204 
5205 QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5206 {
5207  QgsGradientColorRamp expRamp;
5208  const QgsColorRamp *ramp = nullptr;
5209  if ( values.at( 0 ).canConvert<QgsGradientColorRamp>() )
5210  {
5211  expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
5212  ramp = &expRamp;
5213  }
5214  else
5215  {
5216  QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5217  ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
5218  if ( ! ramp )
5219  {
5220  parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
5221  return QVariant();
5222  }
5223  }
5224 
5225  double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5226  QColor color = ramp->color( value );
5227  return QgsSymbolLayerUtils::encodeColor( color );
5228 }
5229 
5230 static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5231 {
5232  // Hue ranges from 0 - 360
5233  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5234  // Saturation ranges from 0 - 100
5235  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5236  // Lightness ranges from 0 - 100
5237  double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5238 
5239  QColor color = QColor::fromHslF( hue, saturation, lightness );
5240 
5241  if ( ! color.isValid() )
5242  {
5243  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
5244  color = QColor( 0, 0, 0 );
5245  }
5246 
5247  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5248 }
5249 
5250 static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5251 {
5252  // Hue ranges from 0 - 360
5253  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5254  // Saturation ranges from 0 - 100
5255  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5256  // Lightness ranges from 0 - 100
5257  double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5258  // Alpha ranges from 0 - 255
5259  double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
5260 
5261  QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
5262  if ( ! color.isValid() )
5263  {
5264  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
5265  color = QColor( 0, 0, 0 );
5266  }
5267  return QgsSymbolLayerUtils::encodeColor( color );
5268 }
5269 
5270 static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5271 {
5272  // Hue ranges from 0 - 360
5273  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5274  // Saturation ranges from 0 - 100
5275  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5276  // Value ranges from 0 - 100
5277  double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5278 
5279  QColor color = QColor::fromHsvF( hue, saturation, value );
5280 
5281  if ( ! color.isValid() )
5282  {
5283  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
5284  color = QColor( 0, 0, 0 );
5285  }
5286 
5287  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5288 }
5289 
5290 static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5291 {
5292  // Hue ranges from 0 - 360
5293  double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5294  // Saturation ranges from 0 - 100
5295  double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5296  // Value ranges from 0 - 100
5297  double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5298  // Alpha ranges from 0 - 255
5299  double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
5300 
5301  QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
5302  if ( ! color.isValid() )
5303  {
5304  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
5305  color = QColor( 0, 0, 0 );
5306  }
5307  return QgsSymbolLayerUtils::encodeColor( color );
5308 }
5309 
5310 static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5311 {
5312  // Cyan ranges from 0 - 100
5313  double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
5314  // Magenta ranges from 0 - 100
5315  double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5316  // Yellow ranges from 0 - 100
5317  double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5318  // Black ranges from 0 - 100
5319  double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
5320 
5321  QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
5322 
5323  if ( ! color.isValid() )
5324  {
5325  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
5326  color = QColor( 0, 0, 0 );
5327  }
5328 
5329  return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5330 }
5331 
5332 static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5333 {
5334  // Cyan ranges from 0 - 100
5335  double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
5336  // Magenta ranges from 0 - 100
5337  double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5338  // Yellow ranges from 0 - 100
5339  double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5340  // Black ranges from 0 - 100
5341  double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
5342  // Alpha ranges from 0 - 255
5343  double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
5344 
5345  QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
5346  if ( ! color.isValid() )
5347  {
5348  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
5349  color = QColor( 0, 0, 0 );
5350  }
5351  return QgsSymbolLayerUtils::encodeColor( color );
5352 }
5353 
5354 static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5355 {
5356  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5357  if ( ! color.isValid() )
5358  {
5359  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5360  return QVariant();
5361  }
5362 
5363  QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5364  if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
5365  return color.red();
5366  else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
5367  return color.green();
5368  else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
5369  return color.blue();
5370  else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
5371  return color.alpha();
5372  else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
5373  return color.hsvHueF() * 360;
5374  else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
5375  return color.hsvSaturationF() * 100;
5376  else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
5377  return color.valueF() * 100;
5378  else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
5379  return color.hslHueF() * 360;
5380  else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
5381  return color.hslSaturationF() * 100;
5382  else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
5383  return color.lightnessF() * 100;
5384  else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
5385  return color.cyanF() * 100;
5386  else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
5387  return color.magentaF() * 100;
5388  else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
5389  return color.yellowF() * 100;
5390  else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
5391  return color.blackF() * 100;
5392 
5393  parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
5394  return QVariant();
5395 }
5396 
5397 static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5398 {
5399  const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
5400  if ( map.count() < 1 )
5401  {
5402  parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
5403  return QVariant();
5404  }
5405 
5406  QList< QColor > colors;
5407  QgsGradientStopsList stops;
5408  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
5409  {
5410  colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
5411  if ( !colors.last().isValid() )
5412  {
5413  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
5414  return QVariant();
5415  }
5416 
5417  double step = it.key().toDouble();
5418  if ( it == map.constBegin() )
5419  {
5420  if ( step != 0.0 )
5421  stops << QgsGradientStop( step, colors.last() );
5422  }
5423  else if ( it == map.constEnd() )
5424  {
5425  if ( step != 1.0 )
5426  stops << QgsGradientStop( step, colors.last() );
5427  }
5428  else
5429  {
5430  stops << QgsGradientStop( step, colors.last() );
5431  }
5432  }
5433  bool discrete = values.at( 1 ).toBool();
5434 
5435  return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
5436 }
5437 
5438 static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5439 {
5440  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5441  if ( ! color.isValid() )
5442  {
5443  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5444  return QVariant();
5445  }
5446 
5447  QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5448  int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5449  if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
5450  color.setRed( value );
5451  else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
5452  color.setGreen( value );
5453  else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
5454  color.setBlue( value );
5455  else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
5456  color.setAlpha( value );
5457  else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
5458  color.setHsv( value, color.hsvSaturation(), color.value(), color.alpha() );
5459  else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
5460  color.setHsvF( color.hsvHueF(), value / 100.0, color.valueF(), color.alphaF() );
5461  else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
5462  color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), value / 100.0, color.alphaF() );
5463  else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
5464  color.setHsl( value, color.hslSaturation(), color.lightness(), color.alpha() );
5465  else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
5466  color.setHslF( color.hslHueF(), value / 100.0, color.lightnessF(), color.alphaF() );
5467  else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
5468  color.setHslF( color.hslHueF(), color.hslSaturationF(), value / 100.0, color.alphaF() );
5469  else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
5470  color.setCmykF( value / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
5471  else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
5472  color.setCmykF( color.cyanF(), value / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
5473  else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
5474  color.setCmykF( color.cyanF(), color.magentaF(), value / 100.0, color.blackF(), color.alphaF() );
5475  else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
5476  color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), value / 100.0, color.alphaF() );
5477  else
5478  {
5479  parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
5480  return QVariant();
5481  }
5482  return QgsSymbolLayerUtils::encodeColor( color );
5483 }
5484 
5485 static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5486 {
5487  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5488  if ( ! color.isValid() )
5489  {
5490  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5491  return QVariant();
5492  }
5493 
5494  color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5495 
5496  return QgsSymbolLayerUtils::encodeColor( color );
5497 }
5498 
5499 static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5500 {
5501  QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5502  if ( ! color.isValid() )
5503  {
5504  parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5505  return QVariant();
5506  }
5507 
5508  color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5509 
5510  return QgsSymbolLayerUtils::encodeColor( color );
5511 }
5512 
5513 static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5514 {
5515  QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
5516  QgsGeometry geom = feat.geometry();
5517  if ( !geom.isNull() )
5518  return QVariant::fromValue( geom );
5519  return QVariant();
5520 }
5521 
5522 static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5523 {
5524  QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5525  QString sAuthId = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5526  QString dAuthId = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5527 
5529  if ( ! s.isValid() )
5530  return QVariant::fromValue( fGeom );
5532  if ( ! d.isValid() )
5533  return QVariant::fromValue( fGeom );
5534 
5536  if ( context )
5537  tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5538  QgsCoordinateTransform t( s, d, tContext );
5539  try
5540  {
5542  return QVariant::fromValue( fGeom );
5543  }
5544  catch ( QgsCsException &cse )
5545  {
5546  QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
5547  return QVariant();
5548  }
5549  return QVariant();
5550 }
5551 
5552 
5553 static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5554 {
5555  QVariant result;
5556  QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
5557  if ( vl )
5558  {
5559  QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
5560 
5561  QgsFeatureRequest req;
5562  req.setFilterFid( fid );
5563  req.setTimeout( 10000 );
5564  req.setRequestMayBeNested( true );
5565  if ( context )
5566  req.setFeedback( context->feedback() );
5567  QgsFeatureIterator fIt = vl->getFeatures( req );
5568 
5569  QgsFeature fet;
5570  if ( fIt.nextFeature( fet ) )
5571  result = QVariant::fromValue( fet );
5572  }
5573 
5574  return result;
5575 }
5576 
5577 static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5578 {
5579  //arguments: 1. layer id / name, 2. key attribute, 3. eq value
5580 
5581  std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), parent );
5582 
5583  //no layer found
5584  if ( !featureSource )
5585  {
5586  return QVariant();
5587  }
5588  QgsFeatureRequest req;
5589  QString cacheValueKey;
5590  if ( values.at( 1 ).type() == QVariant::Map )
5591  {
5592  QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
5593 
5594  QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
5595  QString filterString;
5596  for ( ; i != attributeMap.constEnd(); ++i )
5597  {
5598  if ( !filterString.isEmpty() )
5599  {
5600  filterString.append( " AND " );
5601  }
5602  filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
5603  }
5604  cacheValueKey = QStringLiteral( "getfeature:%1:%2" ).arg( featureSource->id(), filterString );
5605  if ( context && context->hasCachedValue( cacheValueKey ) )
5606  {
5607  return context->cachedValue( cacheValueKey );
5608  }
5609  req.setFilterExpression( filterString );
5610  }
5611  else
5612  {
5613  QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5614  int attributeId = featureSource->fields().lookupField( attribute );
5615  if ( attributeId == -1 )
5616  {
5617  return QVariant();
5618  }
5619 
5620  const QVariant &attVal = values.at( 2 );
5621 
5622  cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
5623  if ( context && context->hasCachedValue( cacheValueKey ) )
5624  {
5625  return context->cachedValue( cacheValueKey );
5626  }
5627 
5629  }
5630  req.setLimit( 1 );
5631  req.setTimeout( 10000 );
5632  req.setRequestMayBeNested( true );
5633  if ( context )
5634  req.setFeedback( context->feedback() );
5635  if ( !parent->needsGeometry() )
5636  {
5638  }
5639  QgsFeatureIterator fIt = featureSource->getFeatures( req );
5640 
5641  QgsFeature fet;
5642  QVariant res;
5643  if ( fIt.nextFeature( fet ) )
5644  {
5645  res = QVariant::fromValue( fet );
5646  }
5647 
5648  if ( context )
5649  context->setCachedValue( cacheValueKey, res );
5650  return res;
5651 }
5652 
5653 static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
5654 {
5655  QVariant result;
5656  QString fieldName;
5657 
5658  if ( context )
5659  {
5660  if ( !values.isEmpty() )
5661  {
5662  QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
5663  if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
5664  fieldName = col->name();
5665  else if ( values.size() == 2 )
5666  fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5667  }
5668 
5669  QVariant value = values.at( 0 );
5670 
5671  const QgsFields fields = context->fields();
5672  int fieldIndex = fields.lookupField( fieldName );
5673 
5674  if ( fieldIndex == -1 )
5675  {
5676  parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
5677  }
5678  else
5679  {
5680  QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
5681 
5682  const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
5683  if ( context->hasCachedValue( cacheValueKey ) )
5684  {
5685  return context->cachedValue( cacheValueKey );
5686  }
5687 
5688  const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
5690 
5691  const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
5692 
5693  QVariant cache;
5694  if ( !context->hasCachedValue( cacheKey ) )
5695  {
5696  cache = formatter->createCache( layer, fieldIndex, setup.config() );
5697  context->setCachedValue( cacheKey, cache );
5698  }
5699  else
5700  cache = context->cachedValue( cacheKey );
5701 
5702  result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
5703 
5704  context->setCachedValue( cacheValueKey, result );
5705  }
5706  }
5707  else
5708  {
5709  parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
5710  }
5711 
5712  return result;
5713 }
5714 
5715 static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5716 {
5717  const QVariant data = values.at( 0 );
5718  const QMimeDatabase db;
5719  return db.mimeTypeForData( data.toByteArray() ).name();
5720 }
5721 
5722 static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5723 {
5724  QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
5725 
5726  if ( !layer )
5727  return QVariant();
5728 
5729  // here, we always prefer the layer metadata values over the older server-specific published values
5730  QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5731  if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
5732  return layer->name();
5733  else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
5734  return layer->id();
5735  else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
5736  return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->title();
5737  else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
5738  return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->abstract();
5739  else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
5740  {
5741  QStringList keywords;
5742  const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
5743  for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
5744  {
5745  keywords.append( it.value() );
5746  }
5747  if ( !keywords.isEmpty() )
5748  return keywords;
5749  return layer->keywordList();
5750  }
5751  else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
5752  return layer->dataUrl();
5753  else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
5754  {
5755  return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->attribution() );
5756  }
5757  else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
5758  return layer->attributionUrl();
5759  else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
5760  return layer->publicSource();
5761  else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
5762  return layer->minimumScale();
5763  else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
5764  return layer->maximumScale();
5765  else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
5766  return layer->isEditable();
5767  else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
5768  return layer->crs().authid();
5769  else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
5770  return layer->crs().toProj();
5771  else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
5772  return layer->crs().description();
5773  else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
5774  {
5775  QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
5776  QVariant result = QVariant::fromValue( extentGeom );
5777  return result;
5778  }
5779  else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
5780  return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
5781  else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
5782  {
5783  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
5784  return decodedUri.value( QStringLiteral( "path" ) );
5785  }
5786  else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
5787  {
5788  switch ( layer->type() )
5789  {
5791  return QCoreApplication::translate( "expressions", "Vector" );
5793  return QCoreApplication::translate( "expressions", "Raster" );
5795  return QCoreApplication::translate( "expressions", "Mesh" );
5797  return QCoreApplication::translate( "expressions", "Vector Tile" );
5799  return QCoreApplication::translate( "expressions", "Plugin" );
5801  return QCoreApplication::translate( "expressions", "Annotation" );
5803  return QCoreApplication::translate( "expressions", "Point Cloud" );
5805  return QCoreApplication::translate( "expressions", "Group" );
5806  }
5807  }
5808  else
5809  {
5810  //vector layer methods
5811  QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
5812  if ( vLayer )
5813  {
5814  if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
5815  return vLayer->storageType();
5816  else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
5818  else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
5819  return QVariant::fromValue( vLayer->featureCount() );
5820  }
5821  }
5822 
5823  return QVariant();
5824 }
5825 
5826 static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5827 {
5828  QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
5829  if ( !layer )
5830  {
5831  parent->setEvalErrorString( QObject::tr( "Cannot find layer %1" ).arg( values.at( 0 ).toString() ) );
5832  return QVariant();
5833  }
5834 
5835  if ( !layer->dataProvider() )
5836  {
5837  parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
5838  return QVariant();
5839  }
5840 
5841  const QString uriPart = values.at( 1 ).toString();
5842 
5843  const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
5844 
5845  if ( !uriPart.isNull() )
5846  {
5847  return decodedUri.value( values.at( 1 ).toString() );
5848  }
5849  else
5850  {
5851  return decodedUri;
5852  }
5853 }
5854 
5855 static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5856 {
5857  QString layerIdOrName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5858 
5859  //try to find a matching layer by name
5860  QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerIdOrName ); //search by id first
5861  if ( !layer )
5862  {
5863  QList<QgsMapLayer *> layersByName = QgsProject::instance()->mapLayersByName( layerIdOrName );
5864  if ( !layersByName.isEmpty() )
5865  {
5866  layer = layersByName.at( 0 );
5867  }
5868  }
5869 
5870  if ( !layer )
5871  return QVariant();
5872 
5873  QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
5874  if ( !rl )
5875  return QVariant();
5876 
5877  int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5878  if ( band < 1 || band > rl->bandCount() )
5879  {
5880  parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer %2" ).arg( band ).arg( layerIdOrName ) );
5881  return QVariant();
5882  }
5883 
5884  QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5885  int stat = 0;
5886 
5887  if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
5888  stat = QgsRasterBandStats::Mean;
5889  else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
5891  else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
5892  stat = QgsRasterBandStats::Min;
5893  else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
5894  stat = QgsRasterBandStats::Max;
5895  else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
5897  else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
5898  stat = QgsRasterBandStats::Sum;
5899  else
5900  {
5901  parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
5902  return QVariant();
5903  }
5904 
5905  QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
5906  switch ( stat )
5907  {
5909  return stats.mean;
5911  return stats.stdDev;
5913  return stats.minimumValue;
5915  return stats.maximumValue;
5917  return stats.range;
5919  return stats.sum;
5920  }
5921  return QVariant();
5922 }
5923 
5924 static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5925 {
5926  return values;
5927 }
5928 
5929 static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5930 {
5931  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5932  bool ascending = values.value( 1 ).toBool();
5933  std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
5934  return list;
5935 }
5936 
5937 static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5938 {
5939  return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
5940 }
5941 
5942 static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5943 {
5944  return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
5945 }
5946 
5947 static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5948 {
5949  return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
5950 }
5951 
5952 static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5953 {
5954  QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5955  QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
5956  int match = 0;
5957  for ( const auto &item : listB )
5958  {
5959  if ( listA.contains( item ) )
5960  match++;
5961  }
5962 
5963  return QVariant( match == listB.count() );
5964 }
5965 
5966 static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5967 {
5968  return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
5969 }
5970 
5971 static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5972 {
5973  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5974  const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5975  if ( pos < list.length() && pos >= 0 ) return list.at( pos );
5976  else if ( pos < 0 && ( list.length() + pos ) >= 0 )
5977  return list.at( list.length() + pos );
5978  return QVariant();
5979 }
5980 
5981 static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5982 {
5983  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5984  return list.value( 0 );
5985 }
5986 
5987 static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5988 {
5989  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5990  return list.value( list.size() - 1 );
5991 }
5992 
5993 static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5994 {
5995  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
5996  return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
5997 }
5998 
5999 static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6000 {
6001  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6002  return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6003 }
6004 
6005 static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6006 {
6007  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6008  int i = 0;
6009  double total = 0.0;
6010  for ( const QVariant &item : list )
6011  {
6012  switch ( item.userType() )
6013  {
6014  case QMetaType::Int:
6015  case QMetaType::UInt:
6016  case QMetaType::LongLong:
6017  case QMetaType::ULongLong:
6018  case QMetaType::Float:
6019  case QMetaType::Double:
6020  total += item.toDouble();
6021  ++i;
6022  break;
6023  }
6024  }
6025  return i == 0 ? QVariant() : total / i;
6026 }
6027 
6028 static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6029 {
6030  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6031  QVariantList numbers;
6032  for ( const auto &item : list )
6033  {
6034  switch ( item.userType() )
6035  {
6036  case QMetaType::Int:
6037  case QMetaType::UInt:
6038  case QMetaType::LongLong:
6039  case QMetaType::ULongLong:
6040  case QMetaType::Float:
6041  case QMetaType::Double:
6042  numbers.append( item );
6043  break;
6044  }
6045  }
6046  std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6047  const int count = numbers.count();
6048  if ( count == 0 )
6049  {
6050  return QVariant();
6051  }
6052  else if ( count % 2 )
6053  {
6054  return numbers.at( count / 2 );
6055  }
6056  else
6057  {
6058  return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
6059  }
6060 }
6061 
6062 static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6063 {
6064  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6065  int i = 0;
6066  double total = 0.0;
6067  for ( const QVariant &item : list )
6068  {
6069  switch ( item.userType() )
6070  {
6071  case QMetaType::Int:
6072  case QMetaType::UInt:
6073  case QMetaType::LongLong:
6074  case QMetaType::ULongLong:
6075  case QMetaType::Float:
6076  case QMetaType::Double:
6077  total += item.toDouble();
6078  ++i;
6079  break;
6080  }
6081  }
6082  return i == 0 ? QVariant() : total;
6083 }
6084 
6085 static QVariant convertToSameType( const QVariant &value, QVariant::Type type )
6086 {
6087  QVariant result = value;
6088  result.convert( static_cast<int>( type ) );
6089  return result;
6090 }
6091 
6092 static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6093 {
6094  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6095  QHash< QVariant, int > hash;
6096  for ( const auto &item : list )
6097  {
6098  ++hash[item];
6099  }
6100  const QList< int > occurrences = hash.values();
6101  if ( occurrences.empty() )
6102  return QVariantList();
6103 
6104  const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
6105 
6106  const QString option = values.at( 1 ).toString();
6107  if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
6108  {
6109  return convertToSameType( hash.keys( maxValue ), values.at( 0 ).type() );
6110  }
6111  else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
6112  {
6113  if ( hash.isEmpty() )
6114  return QVariant();
6115 
6116  return QVariant( hash.keys( maxValue ).first() );
6117  }
6118  else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
6119  {
6120  return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), values.at( 0 ).type() ), context, parent, node );
6121  }
6122  else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
6123  {
6124  if ( maxValue * 2 <= list.size() )
6125  return QVariant();
6126 
6127  return QVariant( hash.keys( maxValue ).first() );
6128  }
6129  else
6130  {
6131  parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
6132  return QVariant();
6133  }
6134 }
6135 
6136 static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6137 {
6138  const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6139  QHash< QVariant, int > hash;
6140  for ( const auto &item : list )
6141  {
6142  ++hash[item];
6143  }
6144  const QList< int > occurrences = hash.values();
6145  if ( occurrences.empty() )
6146  return QVariantList();
6147 
6148  const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
6149 
6150  const QString option = values.at( 1 ).toString();
6151  if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
6152  {
6153  return convertToSameType( hash.keys( minValue ), values.at( 0 ).type() );
6154  }
6155  else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
6156  {
6157  if ( hash.isEmpty() )
6158  return QVariant();
6159 
6160  return QVariant( hash.keys( minValue ).first() );
6161  }
6162  else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
6163  {
6164  return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), values.at( 0 ).type() ), context, parent, node );
6165  }
6166  else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
6167  {
6168  if ( hash.keys().isEmpty() )
6169  return QVariant();
6170 
6171  // Remove the majority, all others are minority
6172  const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
6173  if ( maxValue * 2 > list.size() )
6174  hash.remove( hash.key( maxValue ) );
6175 
6176  return convertToSameType( hash.keys(), values.at( 0 ).type() );
6177  }
6178  else
6179  {
6180  parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
6181  return QVariant();
6182  }
6183 }
6184 
6185 static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6186 {
6187  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6188  list.append( values.at( 1 ) );
6189  return convertToSameType( list, values.at( 0 ).type() );
6190 }
6191 
6192 static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6193 {
6194  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6195  list.prepend( values.at( 1 ) );
6196  return convertToSameType( list, values.at( 0 ).type() );
6197 }
6198 
6199 static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6200 {
6201  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6202  list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
6203  return convertToSameType( list, values.at( 0 ).type() );
6204 }
6205 
6206 static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6207 {
6208  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6209  int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6210  if ( position < 0 )
6211  position = position + list.length();
6212  if ( position >= 0 && position < list.length() )
6213  list.removeAt( position );
6214  return convertToSameType( list, values.at( 0 ).type() );
6215 }
6216 
6217 static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6218 {
6219  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6220  list.removeAll( values.at( 1 ) );
6221  return convertToSameType( list, values.at( 0 ).type() );
6222 }
6223 
6224 static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6225 {
6226  if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
6227  {
6228  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6229 
6230  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6231  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6232  {
6233  int index = list.indexOf( it.key() );
6234  while ( index >= 0 )
6235  {
6236  list.replace( index, it.value() );
6237  index = list.indexOf( it.key() );
6238  }
6239  }
6240 
6241  return convertToSameType( list, values.at( 0 ).type() );
6242  }
6243  else if ( values.count() == 3 )
6244  {
6245  QVariantList before;
6246  QVariantList after;
6247  bool isSingleReplacement = false;
6248 
6249  if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).type() != QVariant::StringList )
6250  {
6251  before = QVariantList() << values.at( 1 );
6252  }
6253  else
6254  {
6255  before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6256  }
6257 
6258  if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
6259  {
6260  after = QVariantList() << values.at( 2 );
6261  isSingleReplacement = true;
6262  }
6263  else
6264  {
6265  after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
6266  }
6267 
6268  if ( !isSingleReplacement && before.length() != after.length() )
6269  {
6270  parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
6271  return QVariant();
6272  }
6273 
6274  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6275  for ( int i = 0; i < before.length(); i++ )
6276  {
6277  int index = list.indexOf( before.at( i ) );
6278  while ( index >= 0 )
6279  {
6280  list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
6281  index = list.indexOf( before.at( i ) );
6282  }
6283  }
6284 
6285  return convertToSameType( list, values.at( 0 ).type() );
6286  }
6287  else
6288  {
6289  parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
6290  return QVariant();
6291  }
6292 }
6293 
6294 static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6295 {
6296  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6297  QVariantList list_new;
6298 
6299  for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
6300  {
6301  while ( list.removeOne( cur ) )
6302  {
6303  list_new.append( cur );
6304  }
6305  }
6306 
6307  list_new.append( list );
6308 
6309  return convertToSameType( list_new, values.at( 0 ).type() );
6310 }
6311 
6312 static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6313 {
6314  QVariantList list;
6315  for ( const QVariant &cur : values )
6316  {
6317  list += QgsExpressionUtils::getListValue( cur, parent );
6318  }
6319  return convertToSameType( list, values.at( 0 ).type() );
6320 }
6321 
6322 static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6323 {
6324  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6325  int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6326  const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6327  int slice_length = 0;
6328  // negative positions means positions taken relative to the end of the array
6329  if ( start_pos < 0 )
6330  {
6331  start_pos = list.length() + start_pos;
6332  }
6333  if ( end_pos >= 0 )
6334  {
6335  slice_length = end_pos - start_pos + 1;
6336  }
6337  else
6338  {
6339  slice_length = list.length() + end_pos - start_pos + 1;
6340  }
6341  //avoid negative lengths in QList.mid function
6342  if ( slice_length < 0 )
6343  {
6344  slice_length = 0;
6345  }
6346  list = list.mid( start_pos, slice_length );
6347  return list;
6348 }
6349 
6350 static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6351 {
6352  QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6353  std::reverse( list.begin(), list.end() );
6354  return list;
6355 }
6356 
6357 static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6358 {
6359  const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6360  const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6361  for ( const QVariant &cur : array2 )
6362  {
6363  if ( array1.contains( cur ) )
6364  return QVariant( true );
6365  }
6366  return QVariant( false );
6367 }
6368 
6369 static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6370 {
6371  QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6372 
6373  QVariantList distinct;
6374 
6375  for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
6376  {
6377  if ( !distinct.contains( *it ) )
6378  {
6379  distinct += ( *it );
6380  }
6381  }
6382 
6383  return distinct;
6384 }
6385 
6386 static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6387 {
6388  QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6389  QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6390  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6391 
6392  QString str;
6393 
6394  for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
6395  {
6396  str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
6397  if ( it != ( array.constEnd() - 1 ) )
6398  {
6399  str += delimiter;
6400  }
6401  }
6402 
6403  return QVariant( str );
6404 }
6405 
6406 static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6407 {
6408  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6409  QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6410  QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6411 
6412  QStringList list = str.split( delimiter );
6413  QVariantList array;
6414 
6415  for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
6416  {
6417  array += ( !( *it ).isEmpty() ) ? *it : empty;
6418  }
6419 
6420  return array;
6421 }
6422 
6423 static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6424 {
6425  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6426  QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
6427  if ( document.isNull() )
6428  return QVariant();
6429 
6430  return document.toVariant();
6431 }
6432 
6433 static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6434 {
6435  Q_UNUSED( parent )
6436  QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
6437  return document.toJson( QJsonDocument::Compact );
6438 }
6439 
6440 static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6441 {
6442  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6443  if ( str.isEmpty() )
6444  return QVariantMap();
6445  str = str.trimmed();
6446 
6447  return QgsHstoreUtils::parse( str );
6448 }
6449 
6450 static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6451 {
6452  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6453  return QgsHstoreUtils::build( map );
6454 }
6455 
6456 static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6457 {
6458  QVariantMap result;
6459  for ( int i = 0; i + 1 < values.length(); i += 2 )
6460  {
6461  result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
6462  }
6463  return result;
6464 }
6465 
6466 static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6467 {
6468  const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6469  const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6470  QVariantMap resultMap;
6471 
6472  for ( auto it = map.cbegin(); it != map.cend(); it++ )
6473  {
6474  resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
6475  }
6476 
6477  return resultMap;
6478 }
6479 
6480 static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6481 {
6482  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
6483 }
6484 
6485 static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6486 {
6487  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
6488 }
6489 
6490 static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6491 {
6492  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6493  map.remove( values.at( 1 ).toString() );
6494  return map;
6495 }
6496 
6497 static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6498 {
6499  QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6500  map.insert( values.at( 1 ).toString(), values.at( 2 ) );
6501  return map;
6502 }
6503 
6504 static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6505 {
6506  QVariantMap result;
6507  for ( const QVariant &cur : values )
6508  {
6509  const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
6510  for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
6511  result.insert( it.key(), it.value() );
6512  }
6513  return result;
6514 }
6515 
6516 static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6517 {
6518  return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
6519 }
6520 
6521 static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6522 {
6523  return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
6524 }
6525 
6526 static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6527 {
6528  QString envVarName = values.at( 0 ).toString();
6529  return QProcessEnvironment::systemEnvironment().value( envVarName );
6530 }
6531 
6532 static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6533 {
6534  const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6535  if ( parent->hasEvalError() )
6536  {
6537  parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "base_file_name" ) ) );
6538  return QVariant();
6539  }
6540  return QFileInfo( file ).completeBaseName();
6541 }
6542 
6543 static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6544 {
6545  const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6546  if ( parent->hasEvalError() )
6547  {
6548  parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_suffix" ) ) );
6549  return QVariant();
6550  }
6551  return QFileInfo( file ).completeSuffix();
6552 }
6553 
6554 static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6555 {
6556  const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6557  if ( parent->hasEvalError() )
6558  {
6559  parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_exists" ) ) );
6560  return QVariant();
6561  }
6562  return QFileInfo::exists( file );
6563 }
6564 
6565 static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6566 {
6567  const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6568  if ( parent->hasEvalError() )
6569  {
6570  parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_name" ) ) );
6571  return QVariant();
6572  }
6573  return QFileInfo( file ).fileName();
6574 }
6575 
6576 static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6577 {
6578  const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6579  if ( parent->hasEvalError() )
6580  {
6581  parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_file" ) ) );
6582  return QVariant();
6583  }
6584  return QFileInfo( file ).isFile();
6585 }
6586 
6587 static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6588 {
6589  const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6590  if ( parent->hasEvalError() )
6591  {
6592  parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_directory" ) ) );
6593  return QVariant();
6594  }
6595  return QFileInfo( file ).isDir();
6596 }
6597 
6598 static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6599 {
6600  const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6601  if ( parent->hasEvalError() )
6602  {
6603  parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_path" ) ) );
6604  return QVariant();
6605  }
6606  return QDir::toNativeSeparators( QFileInfo( file ).path() );
6607 }
6608 
6609 static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6610 {
6611  const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6612  if ( parent->hasEvalError() )
6613  {
6614  parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_size" ) ) );
6615  return QVariant();
6616  }
6617  return QFileInfo( file ).size();
6618 }
6619 
6620 static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
6621 {
6622  return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
6623 }
6624 
6625 static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6626 {
6627  QVariant hash;
6628  QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6629  QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
6630 
6631  if ( method == QLatin1String( "md4" ) )
6632  {
6633  hash = fcnHash( str, QCryptographicHash::Md4 );
6634  }
6635  else if ( method == QLatin1String( "md5" ) )
6636  {
6637  hash = fcnHash( str, QCryptographicHash::Md5 );
6638  }
6639  else if ( method == QLatin1String( "sha1" ) )
6640  {
6641  hash = fcnHash( str, QCryptographicHash::Sha1 );
6642  }
6643  else if ( method == QLatin1String( "sha224" ) )
6644  {
6645  hash = fcnHash( str, QCryptographicHash::Sha224 );
6646  }
6647  else if ( method == QLatin1String( "sha256" ) )
6648  {
6649  hash = fcnHash( str, QCryptographicHash::Sha256 );
6650  }
6651  else if ( method == QLatin1String( "sha384" ) )
6652  {
6653  hash = fcnHash( str, QCryptographicHash::Sha384 );
6654  }
6655  else if ( method == QLatin1String( "sha512" ) )
6656  {
6657  hash = fcnHash( str, QCryptographicHash::Sha512 );
6658  }
6659  else if ( method == QLatin1String( "sha3_224" ) )
6660  {
6661  hash = fcnHash( str, QCryptographicHash::Sha3_224 );
6662  }
6663  else if ( method == QLatin1String( "sha3_256" ) )
6664  {
6665  hash = fcnHash( str, QCryptographicHash::Sha3_256 );
6666  }
6667  else if ( method == QLatin1String( "sha3_384" ) )
6668  {
6669  hash = fcnHash( str, QCryptographicHash::Sha3_384 );
6670  }
6671  else if ( method == QLatin1String( "sha3_512" ) )
6672  {
6673  hash = fcnHash( str, QCryptographicHash::Sha3_512 );
6674  }
6675  else if ( method == QLatin1String( "keccak_224" ) )
6676  {
6677  hash = fcnHash( str, QCryptographicHash::Keccak_224 );
6678  }
6679  else if ( method == QLatin1String( "keccak_256" ) )
6680  {
6681  hash = fcnHash( str, QCryptographicHash::Keccak_256 );
6682  }
6683  else if ( method == QLatin1String( "keccak_384" ) )
6684  {
6685  hash = fcnHash( str, QCryptographicHash::Keccak_384 );
6686  }
6687  else if ( method == QLatin1String( "keccak_512" ) )
6688  {
6689  hash = fcnHash( str, QCryptographicHash::Keccak_512 );
6690  }
6691  else
6692  {
6693  parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
6694  }
6695  return hash;
6696 }
6697 
6698 static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6699 {
6700  return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
6701 }
6702 
6703 static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6704 {
6705  return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
6706 }
6707 
6708 static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6709 {
6710  const QByteArray input = values.at( 0 ).toByteArray();
6711  return QVariant( QString( input.toBase64() ) );
6712 }
6713 
6714 static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6715 {
6716  const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6717  QUrlQuery query;
6718  for ( auto it = map.cbegin(); it != map.cend(); it++ )
6719  {
6720  query.addQueryItem( it.key(), it.value().toString() );
6721  }
6722  return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
6723 }
6724 
6725 static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6726 {
6727  const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6728  const QByteArray base64 = value.toLocal8Bit();
6729  const QByteArray decoded = QByteArray::fromBase64( base64 );
6730  return QVariant( decoded );
6731 }
6732 
6733 typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
6734 
6735 static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const RelationFunction &relationFunction, bool invert = false, double bboxGrow = 0, bool isNearestFunc = false, bool isIntersectsFunc = false )
6736 {
6737 
6738  const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
6739  QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, parent );
6740 
6741  QgsFeatureRequest request;
6742  request.setTimeout( 10000 );
6743  request.setRequestMayBeNested( true );
6744  request.setFeedback( context->feedback() );
6745 
6746  // First parameter is the overlay layer
6747  QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6749 
6750  const bool layerCanBeCached = node->isStatic( parent, context );
6751  QVariant targetLayerValue = node->eval( parent, context );
6753 
6754  // Second parameter is the expression to evaluate (or null for testonly)
6755  node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6757  QString subExpString = node->dump();
6758 
6759  bool testOnly = ( subExpString == "NULL" );
6760  QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, parent );
6761  if ( !targetLayer ) // No layer, no joy
6762  {
6763  parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
6764  return QVariant();
6765  }
6766 
6767  // Third parameter is the filtering expression
6768  node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6770  QString filterString = node->dump();
6771  if ( filterString != "NULL" )
6772  {
6773  request.setFilterExpression( filterString ); //filter cached features
6774  }
6775 
6776  // Fourth parameter is the limit
6777  node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
6779  QVariant limitValue = node->eval( parent, context );
6781  qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
6782 
6783  // Fifth parameter (for nearest only) is the max distance
6784  double max_distance = 0;
6785  if ( isNearestFunc ) //maxdistance param handling
6786  {
6787  node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
6789  QVariant distanceValue = node->eval( parent, context );
6791  max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
6792  }
6793 
6794  // Fifth or sixth (for nearest only) parameter is the cache toggle
6795  node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
6797  QVariant cacheValue = node->eval( parent, context );
6799  bool cacheEnabled = cacheValue.toBool();
6800 
6801  // Sixth parameter (for intersects only) is the min overlap (area or length)
6802  // Seventh parameter (for intersects only) is the min inscribed circle radius
6803  // Eighth parameter (for intersects only) is the return_details
6804  // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
6805  double minOverlap { -1 };
6806  double minInscribedCircleRadius { -1 };
6807  bool returnDetails = false; //#spellok
6808  bool sortByMeasure = false;
6809  bool sortAscending = false;
6810  bool requireMeasures = false;
6811  bool overlapOrRadiusFilter = false;
6812  if ( isIntersectsFunc )
6813  {
6814 
6815  node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
6817  const QVariant minOverlapValue = node->eval( parent, context );
6819  minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
6820  node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
6822  const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
6824  minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
6825 #if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<9
6826  if ( minInscribedCircleRadiusValue != -1 )
6827  {
6828  parent->setEvalErrorString( QObject::tr( "'min_inscribed_circle_radius' is only available when QGIS is built with GEOS >= 3.9." ) );
6829  return QVariant();
6830  }
6831 #endif
6832  node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
6833  // Return measures is only effective when an expression is set
6834  returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
6835  node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
6836  // Sort by measures is only effective when an expression is set
6837  const QString sorting { node->eval( parent, context ).toString().toLower() };
6838  sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
6839  sortAscending = sorting.startsWith( "asc" );
6840  requireMeasures = sortByMeasure || returnDetails; //#spellok
6841  overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
6842  }
6843 
6844 
6845  FEAT_FROM_CONTEXT( context, feat )
6846  const QgsGeometry geometry = feat.geometry();
6847 
6848  if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
6849  {
6850  QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
6851  request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
6852  }
6853 
6854  bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
6855 
6856  QgsRectangle intDomain = geometry.boundingBox();
6857  if ( bboxGrow != 0 )
6858  {
6859  intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
6860  }
6861 
6862  const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
6863 
6864  // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
6865  // Otherwise, it can be toggled by the user
6866  QgsSpatialIndex spatialIndex;
6867  QgsVectorLayer *cachedTarget;
6868  QList<QgsFeature> features;
6869  if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
6870  {
6871  // If the cache (local spatial index) is enabled, we materialize the whole
6872  // layer, then do the request on that layer instead.
6873  const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
6874  const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
6875 
6876  if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
6877  {
6878  cachedTarget = targetLayer->materialize( request );
6879  if ( layerCanBeCached )
6880  context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
6881  }
6882  else
6883  {
6884  cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
6885  }
6886 
6887  if ( !context->hasCachedValue( cacheIndex ) )
6888  {
6889  spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
6890  if ( layerCanBeCached )
6891  context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
6892  }
6893  else
6894  {
6895  spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
6896  }
6897 
6898  QList<QgsFeatureId> fidsList;
6899  if ( isNearestFunc )
6900  {
6901  fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
6902  }
6903  else
6904  {
6905  fidsList = spatialIndex.intersects( intDomain );
6906  }
6907 
6908  QListIterator<QgsFeatureId> i( fidsList );
6909  while ( i.hasNext() )
6910  {
6911  QgsFeatureId fId2 = i.next();
6912  if ( sameLayers && feat.id() == fId2 )
6913  continue;
6914  features.append( cachedTarget->getFeature( fId2 ) );
6915  }
6916 
6917  }
6918  else
6919  {
6920  // If the cache (local spatial index) is not enabled, we directly
6921  // get the features from the target layer
6922  request.setFilterRect( intDomain );
6923  QgsFeatureIterator fit = targetLayer->getFeatures( request );
6924  QgsFeature feat2;
6925  while ( fit.nextFeature( feat2 ) )
6926  {
6927  if ( sameLayers && feat.id() == feat2.id() )
6928  continue;
6929  features.append( feat2 );
6930  }
6931  }
6932 
6933  QgsExpression subExpression;
6934  QgsExpressionContext subContext;
6935  if ( !testOnly )
6936  {
6937  const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
6938  const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
6939 
6940  if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
6941  {
6942  subExpression = QgsExpression( subExpString );
6944  subExpression.prepare( &subContext );
6945  }
6946  else
6947  {
6948  subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
6949  subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
6950  }
6951  }
6952 
6953  // //////////////////////////////////////////////////////////////////
6954  // Helper functions for geometry tests
6955 
6956  // Test function for linestring geometries, returns TRUE if test passes
6957  auto testLinestring = [ = ]( const QgsGeometry intersection, double & overlapValue ) -> bool
6958  {
6959  bool testResult { false };
6960  // For return measures:
6961  QVector<double> overlapValues;
6962  for ( auto it = intersection.const_parts_begin(); ! testResult && it != intersection.const_parts_end(); ++it )
6963  {
6964  const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
6965  // Check min overlap for intersection (if set)
6966  if ( minOverlap != -1 || requireMeasures )
6967  {
6968  overlapValue = geom->length();
6969  overlapValues.append( overlapValue );
6970  if ( minOverlap != -1 )
6971  {
6972  if ( overlapValue >= minOverlap )
6973  {
6974  testResult = true;
6975  }
6976  else
6977  {
6978  continue;
6979  }
6980  }
6981  }
6982  }
6983 
6984  if ( ! overlapValues.isEmpty() )
6985  {
6986  overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
6987  }
6988 
6989  return testResult;
6990  };
6991 
6992  // Test function for polygon geometries, returns TRUE if test passes
6993  auto testPolygon = [ = ]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
6994  {
6995  // overlap and inscribed circle tests must be checked both (if the values are != -1)
6996  bool testResult { false };
6997  // For return measures:
6998  QVector<double> overlapValues;
6999  QVector<double> radiusValues;
7000  for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
7001  {
7002  const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
7003  // Check min overlap for intersection (if set)
7004  if ( minOverlap != -1 || requireMeasures )
7005  {
7006  overlapValue = geom->area();
7007  overlapValues.append( geom->area() );
7008  if ( minOverlap != - 1 )
7009  {
7010  if ( overlapValue >= minOverlap )
7011  {
7012  testResult = true;
7013  }
7014  else
7015  {
7016  continue;
7017  }
7018  }
7019  }
7020 
7021 #if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=9 )
7022  // Check min inscribed circle radius for intersection (if set)
7023  if ( minInscribedCircleRadius != -1 || requireMeasures )
7024  {
7025  const QgsRectangle bbox = geom->boundingBox();
7026  const double width = bbox.width();
7027  const double height = bbox.height();
7028  const double size = width > height ? width : height;
7029  const double tolerance = size / 100.0;
7030  radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
7031  testResult = radiusValue >= minInscribedCircleRadius;
7032  radiusValues.append( radiusValues );
7033  }
7034 #endif
7035  } // end for parts
7036 
7037  // Get the max values
7038  if ( !radiusValues.isEmpty() )
7039  {
7040  radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
7041  }
7042 
7043  if ( ! overlapValues.isEmpty() )
7044  {
7045  overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
7046  }
7047 
7048  return testResult;
7049 
7050  };
7051 
7052 
7053  bool found = false;
7054  int foundCount = 0;
7055  QVariantList results;
7056 
7057  QListIterator<QgsFeature> i( features );
7058  while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
7059  {
7060 
7061  QgsFeature feat2 = i.next();
7062 
7063 
7064  if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
7065  {
7066 
7067  double overlapValue = -1;
7068  double radiusValue = -1;
7069 
7070  if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
7071  {
7072  const QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
7073 
7074  // Depending on the intersection geometry type and on the geometry type of
7075  // the tested geometry we can run different tests and collect different measures
7076  // that can be used for sorting (if required).
7077  switch ( intersection.type() )
7078  {
7079 
7080  case QgsWkbTypes::GeometryType::PolygonGeometry:
7081  {
7082 
7083  // Overlap and inscribed circle tests must be checked both (if the values are != -1)
7084  bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
7085 
7086  if ( ! testResult && overlapOrRadiusFilter )
7087  {
7088  continue;
7089  }
7090 
7091  break;
7092  }
7093 
7094  case QgsWkbTypes::GeometryType::LineGeometry:
7095  {
7096 
7097  // If the intersection is a linestring and a minimum circle is required
7098  // we can discard this result immediately.
7099  if ( minInscribedCircleRadius != -1 )
7100  {
7101  continue;
7102  }
7103 
7104  // Otherwise a test for the overlap value is performed.
7105  const bool testResult { testLinestring( intersection, overlapValue ) };
7106 
7107  if ( ! testResult && overlapOrRadiusFilter )
7108  {
7109  continue;
7110  }
7111 
7112  break;
7113  }
7114 
7115  case QgsWkbTypes::GeometryType::PointGeometry:
7116  {
7117 
7118  // If the intersection is a point and a minimum circle is required
7119  // we can discard this result immediately.
7120  if ( minInscribedCircleRadius != -1 )
7121  {
7122  continue;
7123  }
7124 
7125  bool testResult { false };
7126  if ( minOverlap != -1 || requireMeasures )
7127  {
7128  // Initially set this to 0 because it's a point intersection...
7129  overlapValue = 0;
7130  // ... but if the target geometry is not a point and the source
7131  // geometry is a point, we must record the length or the area
7132  // of the intersected geometry and use that as a measure for
7133  // sorting or reporting.
7134  if ( geometry.type() == QgsWkbTypes::GeometryType::PointGeometry )
7135  {
7136  switch ( feat2.geometry().type() )
7137  {
7138  case QgsWkbTypes::GeometryType::UnknownGeometry:
7139  case QgsWkbTypes::GeometryType::NullGeometry:
7140  case QgsWkbTypes::GeometryType::PointGeometry:
7141  {
7142  break;
7143  }
7144  case QgsWkbTypes::GeometryType::LineGeometry:
7145  {
7146  testResult = testLinestring( feat2.geometry(), overlapValue );
7147  break;
7148  }
7149  case QgsWkbTypes::GeometryType::PolygonGeometry:
7150  {
7151  testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
7152  break;
7153  }
7154  }
7155  }
7156 
7157  if ( ! testResult && overlapOrRadiusFilter )
7158  {
7159  continue;
7160  }
7161 
7162  }
7163  break;
7164  }
7165 
7166  case QgsWkbTypes::GeometryType::NullGeometry:
7167  case QgsWkbTypes::GeometryType::UnknownGeometry:
7168  {
7169  continue;
7170  }
7171  }
7172  }
7173 
7174  found = true;
7175  foundCount++;
7176 
7177  // We just want a single boolean result if there is any intersect: finish and return true
7178  if ( testOnly )
7179  break;
7180 
7181  if ( !invert )
7182  {
7183  // We want a list of attributes / geometries / other expression values, evaluate now
7184  subContext.setFeature( feat2 );
7185  const QVariant expResult = subExpression.evaluate( &subContext );
7186 
7187  if ( requireMeasures )
7188  {
7189  QVariantMap resultRecord;
7190  resultRecord.insert( QStringLiteral( "id" ), feat2.id() );
7191  resultRecord.insert( QStringLiteral( "result" ), expResult );
7192  // Overlap is always added because return measures was set
7193  resultRecord.insert( QStringLiteral( "overlap" ), overlapValue );
7194  // Radius is only added when is different than -1 (because for linestrings is not set)
7195  if ( radiusValue != -1 )
7196  {
7197  resultRecord.insert( QStringLiteral( "radius" ), radiusValue );
7198  }
7199  results.append( resultRecord );
7200  }
7201  else
7202  {
7203  results.append( expResult );
7204  }
7205  }
7206  else
7207  {
7208  // If not, results is a list of found ids, which we'll inverse and evaluate below
7209  results.append( feat2.id() );
7210  }
7211  }
7212  }
7213 
7214  if ( testOnly )
7215  {
7216  if ( invert )
7217  found = !found;//for disjoint condition
7218  return found;
7219  }
7220 
7221  if ( !invert )
7222  {
7223  if ( requireMeasures )
7224  {
7225  if ( sortByMeasure )
7226  {
7227  std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
7228  {
7229  return sortAscending ?
7230  recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble()
7231  : recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble();
7232  } );
7233  }
7234  // Resize
7235  if ( limit > 0 && results.size() > limit )
7236  {
7237  results.erase( results.begin() + limit );
7238  }
7239 
7240  if ( ! returnDetails ) //#spellok
7241  {
7242  QVariantList expResults;
7243  for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
7244  {
7245  expResults.append( it->toMap().value( QStringLiteral( "result" ) ) );
7246  }
7247  return expResults;
7248  }
7249  }
7250 
7251  return results;
7252  }
7253 
7254  // for disjoint condition returns the results for cached layers not intersected feats
7255  QVariantList disjoint_results;
7256  QgsFeature feat2;
7257  QgsFeatureRequest request2;
7258  request2.setLimit( limit );
7259  if ( context )
7260  request2.setFeedback( context->feedback() );
7261  QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
7262  while ( fi.nextFeature( feat2 ) )
7263  {
7264  if ( !results.contains( feat2.id() ) )
7265  {
7266  subContext.setFeature( feat2 );
7267  disjoint_results.append( subExpression.evaluate( &subContext ) );
7268  }
7269  }
7270  return disjoint_results;
7271 
7272 }
7273 
7274 // Intersect functions:
7275 
7276 static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7277 {
7278  return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
7279 }
7280 
7281 static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7282 {
7283  return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
7284 }
7285 
7286 static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7287 {
7288  return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
7289 }
7290 
7291 static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7292 {
7293  return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
7294 }
7295 
7296 static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7297 {
7298  return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
7299 }
7300 
7301 static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7302 {
7303  return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
7304 }
7305 
7306 static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7307 {
7308  return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
7309 }
7310 
7311 static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7312 {
7313  return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
7314 }
7315 
7316 const QList<QgsExpressionFunction *> &QgsExpression::Functions()
7317 {
7318  // The construction of the list isn't thread-safe, and without the mutex,
7319  // crashes in the WFS provider may occur, since it can parse expressions
7320  // in parallel.
7321  // The mutex needs to be recursive.
7322 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
7323  static QMutex sFunctionsMutex( QMutex::Recursive );
7324  QMutexLocker locker( &sFunctionsMutex );
7325 #else
7326  static QRecursiveMutex sFunctionsMutex;
7327  QMutexLocker locker( &sFunctionsMutex );
7328 #endif
7329 
7330  QList<QgsExpressionFunction *> &functions = *sFunctions();
7331 
7332  if ( functions.isEmpty() )
7333  {
7335  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
7336  << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
7337  << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
7338 
7339  QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
7340  aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
7341  << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
7342 
7343  QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
7344  aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
7345 
7346  functions
7347  << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
7348  << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
7349  << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
7350  << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
7351  << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
7352  << 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" ) )
7353  << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
7354  << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
7355  << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
7356  << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
7357  << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
7358  << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
7359  << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
7360  << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
7361  << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
7362  << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
7363  << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
7364  << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
7365  << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
7366 
7367  QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
7368  randFunc->setIsStatic( false );
7369  functions << randFunc;
7370 
7371  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" ) );
7372  randfFunc->setIsStatic( false );
7373  functions << randfFunc;
7374 
7375  functions
7376  << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
7377  << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
7378  << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
7379  << 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" ) )
7380  << 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" ) )
7381  << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
7382  << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
7383  << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
7384  << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
7385  << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
7386  << 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" ) )
7387  << 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" ) )
7388  << 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" ) )
7389  << 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" ) )
7390  << 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" ) )
7391  << 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" ) )
7392  << 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" ) )
7393  << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
7394  << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
7395  << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
7396  << 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 )
7397  << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
7398 
7399  << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
7401  << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7402  << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
7403  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
7404  << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
7405  << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
7406  << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
7407  fcnAggregate,
7408  QStringLiteral( "Aggregates" ),
7409  QString(),
7410  []( const QgsExpressionNodeFunction * node )
7411  {
7412  // usesGeometry callback: return true if @parent variable is referenced
7413 
7414  if ( !node )
7415  return true;
7416 
7417  if ( !node->args() )
7418  return false;
7419 
7420  QSet<QString> referencedVars;
7421  if ( node->args()->count() > 2 )
7422  {
7423  QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
7424  referencedVars = subExpressionNode->referencedVariables();
7425  }
7426 
7427  if ( node->args()->count() > 3 )
7428  {
7429  QgsExpressionNode *filterNode = node->args()->at( 3 );
7430  referencedVars.unite( filterNode->referencedVariables() );
7431  }
7432  return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
7433  },
7434  []( const QgsExpressionNodeFunction * node )
7435  {
7436  // referencedColumns callback: return AllAttributes if @parent variable is referenced
7437 
7438  if ( !node )
7439  return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
7440 
7441  if ( !node->args() )
7442  return QSet<QString>();
7443 
7444  QSet<QString> referencedCols;
7445  QSet<QString> referencedVars;
7446 
7447  if ( node->args()->count() > 2 )
7448  {
7449  QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
7450  referencedVars = subExpressionNode->referencedVariables();
7451  referencedCols = subExpressionNode->referencedColumns();
7452  }
7453  if ( node->args()->count() > 3 )
7454  {
7455  QgsExpressionNode *filterNode = node->args()->at( 3 );
7456  referencedVars = filterNode->referencedVariables();
7457  referencedCols.unite( filterNode->referencedColumns() );
7458  }
7459 
7460  if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
7461  return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
7462  else
7463  return referencedCols;
7464  },
7465  true
7466  )
7467 
7468  << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
7469  << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
7470  << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
7471  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
7472  << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
7473  << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
7474  fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
7475 
7476  << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7477  << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7478  << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7479  << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7480  << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7481  << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7482  << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7483  << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7484  << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7485  << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7486  << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7487  << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7488  << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7489  << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7490  << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7491  << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7492  << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7493  << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7494  << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7495  << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7496  << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7497 
7498  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
7499  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
7500 
7501  << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
7502  << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
7503  << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
7504  fcnAge, QStringLiteral( "Date and Time" ) )
7505  << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
7506  << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
7507  << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
7508  << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
7509  << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
7510  << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
7511  << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
7512  << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
7513  << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
7514  << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
7515  << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
7516  << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
7517  << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
7518  fcnMakeDate, QStringLiteral( "Date and Time" ) )
7519  << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
7520  << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
7521  << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
7522  fcnMakeTime, QStringLiteral( "Date and Time" ) )
7523  << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
7524  << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
7525  << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
7526  << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
7527  << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
7528  << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
7529  fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
7530  << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
7531  << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
7532  << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
7533  << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
7534  << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
7535  << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
7536  << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
7537  fcnMakeInterval, QStringLiteral( "Date and Time" ) )
7538  << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
7539  << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
7540  << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
7541  << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
7542  << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
7543  << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
7544  << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
7545  << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
7546  << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
7547  << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
7548  << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
7549  << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
7550  << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
7551  << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
7552  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
7553  << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
7554  << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
7555  << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
7556  false, QSet< QString >(), false, QStringList(), true )
7557  << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
7558  << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
7559  << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
7560  << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
7561  << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
7562  << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
7563  << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
7564  << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnFormatNumber, QStringLiteral( "String" ) )
7565  << 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" ) )
7566  << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
7567  << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
7568  << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
7569  << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
7570  fcnColorMixRgb, QStringLiteral( "Color" ) )
7571  << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
7572  << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
7573  << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
7574  fcnColorRgb, QStringLiteral( "Color" ) )
7575  << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
7576  << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
7577  << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
7578  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7579  fncColorRgba, QStringLiteral( "Color" ) )
7580  << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
7581  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
7582  fcnRampColor, QStringLiteral( "Color" ) )
7583  << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
7584  << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
7585  fcnCreateRamp, QStringLiteral( "Color" ) )
7586  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7587  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7588  << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
7589  fcnColorHsl, QStringLiteral( "Color" ) )
7590  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7591  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7592  << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
7593  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7594  fncColorHsla, QStringLiteral( "Color" ) )
7595  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7596  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7597  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
7598  fcnColorHsv, QStringLiteral( "Color" ) )
7599  << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7600  << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7601  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
7602  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7603  fncColorHsva, QStringLiteral( "Color" ) )
7604  << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
7605  << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
7606  << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
7607  << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
7608  fcnColorCmyk, QStringLiteral( "Color" ) )
7609  << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
7610  << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
7611  << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
7612  << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
7613  << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7614  fncColorCmyka, QStringLiteral( "Color" ) )
7615  << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
7616  << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
7617  fncColorPart, QStringLiteral( "Color" ) )
7618  << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
7619  << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
7620  fncDarker, QStringLiteral( "Color" ) )
7621  << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
7622  << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
7623  fncLighter, QStringLiteral( "Color" ) )
7624  << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
7625 
7626  // file info
7627  << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7628  fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
7629  << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7630  fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
7631  << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7632  fcnFileExists, QStringLiteral( "Files and Paths" ) )
7633  << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7634  fcnFileName, QStringLiteral( "Files and Paths" ) )
7635  << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7636  fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
7637  << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7638  fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
7639  << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7640  fcnFilePath, QStringLiteral( "Files and Paths" ) )
7641  << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7642  fcnFileSize, QStringLiteral( "Files and Paths" ) )
7643 
7644  << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
7645  fcnExif, QStringLiteral( "Files and Paths" ) )
7646  << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7647  fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
7648 
7649  // hash
7650  << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
7651  fcnGenericHash, QStringLiteral( "Conversions" ) )
7652  << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
7653  fcnHashMd5, QStringLiteral( "Conversions" ) )
7654  << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
7655  fcnHashSha256, QStringLiteral( "Conversions" ) )
7656 
7657  //base64
7658  << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
7659  fcnToBase64, QStringLiteral( "Conversions" ) )
7660  << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
7661  fcnFromBase64, QStringLiteral( "Conversions" ) )
7662 
7663  // deprecated stuff - hidden from users
7664  << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
7665 
7666  QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
7667  geomFunc->setIsStatic( false );
7668  functions << geomFunc;
7669 
7670  QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
7671  areaFunc->setIsStatic( false );
7672  functions << areaFunc;
7673 
7674  functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
7675 
7676  QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
7677  lengthFunc->setIsStatic( false );
7678  functions << lengthFunc;
7679 
7680  QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
7681  perimeterFunc->setIsStatic( false );
7682  functions << perimeterFunc;
7683 
7684  functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
7685 
7686  functions << new QgsStaticExpressionFunction( QStringLiteral( "roundness" ),
7687  QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7688  fcnRoundness, QStringLiteral( "GeometryGroup" ) );
7689 
7690  QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
7691  xFunc->setIsStatic( false );
7692  functions << xFunc;
7693 
7694  QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
7695  yFunc->setIsStatic( false );
7696  functions << yFunc;
7697 
7698  QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
7699  zFunc->setIsStatic( false );
7700  functions << zFunc;
7701 
7702  QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
7703  {
7704  { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
7705  { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
7706  { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
7707  { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
7708  { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
7709  { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
7710  { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
7711  };
7712  QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
7713  while ( i.hasNext() )
7714  {
7715  i.next();
7717  << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7718  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
7719  << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
7720  << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
7721  << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false )
7722  << QgsExpressionFunction::Parameter( QStringLiteral( "min_overlap" ), true, QVariant( -1 ), false )
7723  << QgsExpressionFunction::Parameter( QStringLiteral( "min_inscribed_circle_radius" ), true, QVariant( -1 ), false )
7724  << QgsExpressionFunction::Parameter( QStringLiteral( "return_details" ), true, false, false )
7725  << QgsExpressionFunction::Parameter( QStringLiteral( "sort_by_intersection_size" ), true, QString(), false ),
7726  i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
7727 
7728  // The current feature is accessed for the geometry, so this should not be cached
7729  fcnGeomOverlayFunc->setIsStatic( false );
7730  functions << fcnGeomOverlayFunc;
7731  }
7732 
7733  QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
7734  << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7735  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
7736  << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
7737  << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
7738  << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
7739  << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
7740  fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
7741  // The current feature is accessed for the geometry, so this should not be cached
7742  fcnGeomOverlayNearestFunc->setIsStatic( false );
7743  functions << fcnGeomOverlayNearestFunc;
7744 
7745  functions
7746  << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
7747  << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
7748  << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
7749  << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
7750  << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
7751  << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
7752  << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
7753  << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
7754  << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7755  << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
7756  fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
7757  << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
7758  << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
7759  << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
7760  << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
7761  << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
7762  << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
7763  fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
7764  << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
7765  << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
7766  << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
7767  << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
7768  << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
7769  fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
7770  << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
7771  << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7772  << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
7773  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
7774  fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
7775  << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
7776  << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7777  << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
7778  << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
7779  << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
7780  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
7781  fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
7782  << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
7783  << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7784  << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
7785  << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
7786  << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
7787  fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
7788  << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
7789  << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
7790  << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
7791  fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
7792  << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
7793  << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
7794  << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
7795  << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
7796  << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
7797  fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) );
7798  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" ) );
7799  xAtFunc->setIsStatic( false );
7800  functions << xAtFunc;
7801 
7802  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" ) );
7803  yAtFunc->setIsStatic( false );
7804  functions << yAtFunc;
7805 
7806  functions
7807  << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
7808  << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
7809  << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
7810  << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
7811  << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
7812  << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
7813  << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
7814  << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
7815  << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
7816  << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
7817  << 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" ) )
7818  << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7819  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7820  fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
7821  << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7822  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7823  fcnIntersects, QStringLiteral( "GeometryGroup" ) )
7824  << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7825  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7826  fcnTouches, QStringLiteral( "GeometryGroup" ) )
7827  << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7828  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7829  fcnCrosses, QStringLiteral( "GeometryGroup" ) )
7830  << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7831  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7832  fcnContains, QStringLiteral( "GeometryGroup" ) )
7833  << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7834  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7835  fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
7836  << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7837  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7838  fcnWithin, QStringLiteral( "GeometryGroup" ) )
7839  << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7840  << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
7841  << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
7842  fcnTranslate, QStringLiteral( "GeometryGroup" ) )
7843  << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7844  << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
7845  << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true )
7846  << QgsExpressionFunction::Parameter( QStringLiteral( "per_part" ), true, false ),
7847  fcnRotate, QStringLiteral( "GeometryGroup" ) )
7848  << new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7849  << QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
7850  << QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
7851  << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
7852  fcnScale, QStringLiteral( "GeometryGroup" ) )
7853  << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7854  << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
7855  << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
7856  << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
7857  << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
7858  << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
7859  << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
7860  << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
7861  << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
7862  << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
7863  fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
7864  << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7865  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
7866  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
7867  << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
7868  << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
7869  << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
7870  fcnBuffer, QStringLiteral( "GeometryGroup" ) )
7871  << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7872  fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
7873  << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_cw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7874  fcnForcePolygonCW, QStringLiteral( "GeometryGroup" ) )
7875  << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_ccw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7876  fcnForcePolygonCCW, QStringLiteral( "GeometryGroup" ) )
7877  << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7878  << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
7879  << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
7880  << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
7881  << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
7882  << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7883  << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
7884  << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
7885  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
7886  , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
7887  << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7888  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
7889  , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
7890  << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7891  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
7892  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
7893  << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
7894  << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
7895  fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
7896  << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7897  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
7898  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
7899  << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
7900  << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
7901  fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
7902  << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7903  << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
7904  << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
7905  fcnExtend, QStringLiteral( "GeometryGroup" ) )
7906  << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
7907  << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
7908  << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7909  << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
7910  << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, QStringLiteral( "GeometryGroup" ) )
7911  << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
7912  << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7913  << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
7914  fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
7915  << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7916  << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
7917  fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
7918  << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
7919  << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
7920  << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
7921  << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
7922  << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
7923  << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
7924  << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
7925  << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
7926  << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
7927  << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave" ),
7928  {
7929  QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7930  QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
7931  QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
7932  QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
7933  }, fcnTriangularWave, QStringLiteral( "GeometryGroup" ) )
7934  << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave_randomized" ),
7935  {
7936  QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7937  QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
7938  QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
7939  QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
7940  QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
7941  QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
7942  }, fcnTriangularWaveRandomized, QStringLiteral( "GeometryGroup" ) )
7943  << new QgsStaticExpressionFunction( QStringLiteral( "square_wave" ),
7944  {
7945  QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7946  QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
7947  QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
7948  QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
7949  }, fcnSquareWave, QStringLiteral( "GeometryGroup" ) )
7950  << new QgsStaticExpressionFunction( QStringLiteral( "square_wave_randomized" ),
7951  {
7952  QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7953  QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
7954  QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
7955  QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
7956  QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
7957  QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
7958  }, fcnSquareWaveRandomized, QStringLiteral( "GeometryGroup" ) )
7959  << new QgsStaticExpressionFunction( QStringLiteral( "wave" ),
7960  {
7961  QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7962  QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
7963  QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
7964  QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
7965  }, fcnRoundWave, QStringLiteral( "GeometryGroup" ) )
7966  << new QgsStaticExpressionFunction( QStringLiteral( "wave_randomized" ),
7967  {
7968  QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7969  QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
7970  QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
7971  QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
7972  QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
7973  QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
7974  }, fcnRoundWaveRandomized, QStringLiteral( "GeometryGroup" ) )
7975  << new QgsStaticExpressionFunction( QStringLiteral( "apply_dash_pattern" ),
7976  {
7977  QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7978  QgsExpressionFunction::Parameter( QStringLiteral( "pattern" ) ),
7979  QgsExpressionFunction::Parameter( QStringLiteral( "start_rule" ), true, QStringLiteral( "no_rule" ) ),
7980  QgsExpressionFunction::Parameter( QStringLiteral( "end_rule" ), true, QStringLiteral( "no_rule" ) ),
7981  QgsExpressionFunction::Parameter( QStringLiteral( "adjustment" ), true, QStringLiteral( "both" ) ),
7982  QgsExpressionFunction::Parameter( QStringLiteral( "pattern_offset" ), true, 0 ),
7983  }, fcnApplyDashPattern, QStringLiteral( "GeometryGroup" ) )
7984  << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_count" ),
7985  {
7986  QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7987  QgsExpressionFunction::Parameter( QStringLiteral( "vertices" ) )
7988  }, fcnDensifyByCount, QStringLiteral( "GeometryGroup" ) )
7989  << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_distance" ),
7990  {
7991  QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7992  QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
7993  }, fcnDensifyByDistance, QStringLiteral( "GeometryGroup" ) )
7994  << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
7995  << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
7996  << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
7997  << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
7998  << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
7999  << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
8000  << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
8001  << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
8002  << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
8003  << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8004  << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
8005  << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
8006  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8007  fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
8008  << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
8009  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8010  fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
8011  << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
8012  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8013  << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8014  fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
8015  << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8016  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8017  fcnDifference, QStringLiteral( "GeometryGroup" ) )
8018  << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8019  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8020  fcnDistance, QStringLiteral( "GeometryGroup" ) )
8021  << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
8022  << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
8023  fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
8024  << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8025  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8026  fcnIntersection, QStringLiteral( "GeometryGroup" ) )
8027  << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8028  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8029  fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
8030  << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8031  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8032  fcnCombine, QStringLiteral( "GeometryGroup" ) )
8033  << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8034  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8035  fcnCombine, QStringLiteral( "GeometryGroup" ) )
8036  << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8037  << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
8038  fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
8039  << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8040  fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
8041  << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
8042  << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8043  << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
8044  << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
8045  fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
8046  << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8047  << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
8048  << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
8049  fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
8050  << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8051  fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
8052  << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8053  fcnZMax, QStringLiteral( "GeometryGroup" ) )
8054  << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8055  fcnZMin, QStringLiteral( "GeometryGroup" ) )
8056  << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8057  fcnMMax, QStringLiteral( "GeometryGroup" ) )
8058  << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8059  fcnMMin, QStringLiteral( "GeometryGroup" ) )
8060  << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8061  fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
8062  << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8063  fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
8064 
8065 
8066  QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8067  << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
8068  << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
8069  fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
8070 
8071  orderPartsFunc->setIsStaticFunction(
8072  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8073  {
8074  const QList< QgsExpressionNode *> argList = node->args()->list();
8075  for ( QgsExpressionNode *argNode : argList )
8076  {
8077  if ( !argNode->isStatic( parent, context ) )
8078  return false;
8079  }
8080 
8081  if ( node->args()->count() > 1 )
8082  {
8083  QgsExpressionNode *argNode = node->args()->at( 1 );
8084 
8085  QString expString = argNode->eval( parent, context ).toString();
8086 
8087  QgsExpression e( expString );
8088 
8089  if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
8090  return true;
8091  }
8092 
8093  return true;
8094  } );
8095 
8096  orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8097  {
8098  if ( node->args()->count() > 1 )
8099  {
8100  QgsExpressionNode *argNode = node->args()->at( 1 );
8101  QString expression = argNode->eval( parent, context ).toString();
8102  QgsExpression e( expression );
8103  e.prepare( context );
8104  context->setCachedValue( expression, QVariant::fromValue( e ) );
8105  }
8106  return true;
8107  }
8108  );
8109  functions << orderPartsFunc;
8110 
8111  functions
8112  << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8113  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8114  fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
8115  << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8116  << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8117  fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
8118  << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8119  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
8120  << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8121  << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
8122  << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8123  << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
8124  << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8125  << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
8126  << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8127  << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
8128  << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8129  << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
8130 
8131 
8132  // **Record** functions
8133 
8134  QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
8135  idFunc->setIsStatic( false );
8136  functions << idFunc;
8137 
8138  QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
8139  currentFeatureFunc->setIsStatic( false );
8140  functions << currentFeatureFunc;
8141 
8142  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" ) );
8143  uuidFunc->setIsStatic( false );
8144  functions << uuidFunc;
8145 
8146  functions
8147  << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8148  << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
8149  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ), true ),
8150  fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
8151  << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8152  << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
8153  fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
8154 
8155  QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
8156  fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8157  attributesFunc->setIsStatic( false );
8158  functions << attributesFunc;
8159  QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
8160  fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8161  representAttributesFunc->setIsStatic( false );
8162  functions << representAttributesFunc;
8163 
8165  QStringLiteral( "maptip" ),
8166  -1,
8167  fcnFeatureMaptip,
8168  QStringLiteral( "Record and Attributes" ),
8169  QString(),
8170  false,
8171  QSet<QString>()
8172  );
8173  maptipFunc->setIsStatic( false );
8174  functions << maptipFunc;
8175 
8177  QStringLiteral( "display_expression" ),
8178  -1,
8179  fcnFeatureDisplayExpression,
8180  QStringLiteral( "Record and Attributes" ),
8181  QString(),
8182  false,
8183  QSet<QString>()
8184  );
8185  displayFunc->setIsStatic( false );
8186  functions << displayFunc;
8187 
8189  QStringLiteral( "is_selected" ),
8190  -1,
8191  fcnIsSelected,
8192  QStringLiteral( "Record and Attributes" ),
8193  QString(),
8194  false,
8195  QSet<QString>()
8196  );
8197  isSelectedFunc->setIsStatic( false );
8198  functions << isSelectedFunc;
8199 
8200  functions
8202  QStringLiteral( "num_selected" ),
8203  -1,
8204  fcnNumSelected,
8205  QStringLiteral( "Record and Attributes" ),
8206  QString(),
8207  false,
8208  QSet<QString>()
8209  );
8210 
8211  functions
8213  QStringLiteral( "sqlite_fetch_and_increment" ),
8215  << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
8216  << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
8217  << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
8218  << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
8219  << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
8220  << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
8221  fcnSqliteFetchAndIncrement,
8222  QStringLiteral( "Record and Attributes" )
8223  );
8224 
8225  // **Fields and Values** functions
8226  QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
8227 
8228  representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8229  {
8230  Q_UNUSED( context )
8231  if ( node->args()->count() == 1 )
8232  {
8233  QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
8234  if ( colRef )
8235  {
8236  return true;
8237  }
8238  else
8239  {
8240  parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
8241  return false;
8242  }
8243  }
8244  else if ( node->args()->count() == 2 )
8245  {
8246  return true;
8247  }
8248  else
8249  {
8250  parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
8251  return false;
8252  }
8253  }
8254  );
8255 
8256  functions << representValueFunc;
8257 
8258  // **General** functions
8259  functions
8260  << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8261  << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
8262  fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
8263  << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
8265  << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8266  << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
8267  fcnDecodeUri, QStringLiteral( "Map Layers" ) )
8268  << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
8270  << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
8271  fcnMimeType, QStringLiteral( "General" ) )
8272  << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8273  << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
8274  << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
8275 
8276  // **var** function
8277  QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
8278  varFunction->setIsStaticFunction(
8279  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8280  {
8281  /* A variable node is static if it has a static name and the name can be found at prepare
8282  * time and is tagged with isStatic.
8283  * It is not static if a variable is set during iteration or not tagged isStatic.
8284  * (e.g. geom_part variable)
8285  */
8286  if ( node->args()->count() > 0 )
8287  {
8288  QgsExpressionNode *argNode = node->args()->at( 0 );
8289 
8290  if ( !argNode->isStatic( parent, context ) )
8291  return false;
8292 
8293  QString varName = argNode->eval( parent, context ).toString();
8294 
8295  const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
8296  return scope ? scope->isStatic( varName ) : false;
8297  }
8298  return false;
8299  }
8300  );
8301 
8302  functions
8303  << varFunction;
8304 
8305  functions << new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true );
8306 
8307  QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8308  evalFunc->setIsStaticFunction(
8309  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8310  {
8311  if ( node->args()->count() > 0 )
8312  {
8313  QgsExpressionNode *argNode = node->args()->at( 0 );
8314 
8315  if ( argNode->isStatic( parent, context ) )
8316  {
8317  QString expString = argNode->eval( parent, context ).toString();
8318 
8319  QgsExpression e( expString );
8320 
8321  if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
8322  return true;
8323  }
8324  }
8325 
8326  return false;
8327  } );
8328 
8329  functions << evalFunc;
8330 
8331  QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8332  attributeFunc->setIsStaticFunction(
8333  []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8334  {
8335  const QList< QgsExpressionNode *> argList = node->args()->list();
8336  for ( QgsExpressionNode *argNode : argList )
8337  {
8338  if ( !argNode->isStatic( parent, context ) )
8339  return false;
8340  }
8341 
8342  if ( node->args()->count() == 1 )
8343  {
8344  // not static -- this is the variant which uses the current feature taken direct from the expression context
8345  return false;
8346  }
8347 
8348  return true;
8349  } );
8350  functions << attributeFunc;
8351 
8352  functions
8353  << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
8355  << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
8356 
8357  // functions for arrays
8360  << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8361  << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
8362  << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
8363  << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
8364  << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
8365  << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
8366  << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
8367  << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
8368  << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
8369  << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
8370  << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
8371  << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
8372  << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
8373  << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
8374  << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
8375  << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
8376  << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
8377  << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
8378  << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
8379  << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
8380  << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
8381  << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ) )
8382  << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
8383  << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
8384  << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
8385  << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
8386  << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
8387  << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
8388  << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
8389  << 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" ) )
8390  << 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" ) )
8391  << 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" ) )
8392 
8393  //functions for maps
8394  << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
8395  << new QgsStaticExpressionFunction( QStringLiteral( "to_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "json_string" ) ), fcnWriteJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "map_to_json" ) )
8396  << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
8397  << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
8398  << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
8399  << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
8400  << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
8401  << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
8402  << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
8403  << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
8404  << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
8405  << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
8406  << new QgsStaticExpressionFunction( QStringLiteral( "map_prefix_keys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
8407  << QgsExpressionFunction::Parameter( QStringLiteral( "prefix" ) ),
8408  fcnMapPrefixKeys, QStringLiteral( "Maps" ) )
8409  << new QgsStaticExpressionFunction( QStringLiteral( "url_encode" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
8410  fcnToFormUrlEncode, QStringLiteral( "Maps" ) )
8411 
8412  ;
8413 
8415 
8416  //QgsExpression has ownership of all built-in functions
8417  for ( QgsExpressionFunction *func : std::as_const( functions ) )
8418  {
8419  *sOwnedFunctions() << func;
8420  *sBuiltinFunctions() << func->name();
8421  sBuiltinFunctions()->append( func->aliases() );
8422  }
8423  }
8424  return functions;
8425 }
8426 
8427 bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
8428 {
8429  int fnIdx = functionIndex( function->name() );
8430  if ( fnIdx != -1 )
8431  {
8432  return false;
8433  }
8434  sFunctions()->append( function );
8435  if ( transferOwnership )
8436  sOwnedFunctions()->append( function );
8437  return true;
8438 }
8439 
8440 bool QgsExpression::unregisterFunction( const QString &name )
8441 {
8442  // You can never override the built in functions.
8443  if ( QgsExpression::BuiltinFunctions().contains( name ) )
8444  {
8445  return false;
8446  }
8447  int fnIdx = functionIndex( name );
8448  if ( fnIdx != -1 )
8449  {
8450  sFunctions()->removeAt( fnIdx );
8451  return true;
8452  }
8453  return false;
8454 }
8455 
8457 {
8458  qDeleteAll( *sOwnedFunctions() );
8459  sOwnedFunctions()->clear();
8460 }
8461 
8463 {
8464  if ( sBuiltinFunctions()->isEmpty() )
8465  {
8466  Functions(); // this method builds the gmBuiltinFunctions as well
8467  }
8468  return *sBuiltinFunctions();
8469 }
8470 
8471 
8473  : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
8474  << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
8475  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
8476  QStringLiteral( "Arrays" ) )
8477 {
8478 
8479 }
8480 
8482 {
8483  bool isStatic = false;
8484 
8485  QgsExpressionNode::NodeList *args = node->args();
8486 
8487  if ( args->count() < 2 )
8488  return false;
8489 
8490  if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
8491  {
8492  isStatic = true;
8493  }
8494  return isStatic;
8495 }
8496 
8498 {
8499  Q_UNUSED( node )
8500  QVariantList result;
8501 
8502  if ( args->count() < 2 )
8503  // error
8504  return result;
8505 
8506  QVariantList array = args->at( 0 )->eval( parent, context ).toList();
8507 
8508  QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
8509  std::unique_ptr< QgsExpressionContext > tempContext;
8510  if ( !subContext )
8511  {
8512  tempContext = std::make_unique< QgsExpressionContext >();
8513  subContext = tempContext.get();
8514  }
8515 
8517  subContext->appendScope( subScope );
8518 
8519  for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8520  {
8521  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
8522  result << args->at( 1 )->eval( parent, subContext );
8523  }
8524 
8525  if ( context )
8526  delete subContext->popScope();
8527 
8528  return result;
8529 }
8530 
8531 QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
8532 {
8533  // This is a dummy function, all the real handling is in run
8534  Q_UNUSED( values )
8535  Q_UNUSED( context )
8536  Q_UNUSED( parent )
8537  Q_UNUSED( node )
8538 
8539  Q_ASSERT( false );
8540  return QVariant();
8541 }
8542 
8544 {
8545  QgsExpressionNode::NodeList *args = node->args();
8546 
8547  if ( args->count() < 2 )
8548  // error
8549  return false;
8550 
8551  args->at( 0 )->prepare( parent, context );
8552 
8553  QgsExpressionContext subContext;
8554  if ( context )
8555  subContext = *context;
8556 
8558  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
8559  subContext.appendScope( subScope );
8560 
8561  args->at( 1 )->prepare( parent, &subContext );
8562 
8563  return true;
8564 }
8565 
8567  : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
8568  << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
8569  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
8570  << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
8571  QStringLiteral( "Arrays" ) )
8572 {
8573 
8574 }
8575 
8577 {
8578  bool isStatic = false;
8579 
8580  QgsExpressionNode::NodeList *args = node->args();
8581 
8582  if ( args->count() < 2 )
8583  return false;
8584 
8585  if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
8586  {
8587  isStatic = true;
8588  }
8589  return isStatic;
8590 }
8591 
8593 {
8594  Q_UNUSED( node )
8595  QVariantList result;
8596 
8597  if ( args->count() < 2 )
8598  // error
8599  return result;
8600 
8601  const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
8602 
8603  QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
8604  std::unique_ptr< QgsExpressionContext > tempContext;
8605  if ( !subContext )
8606  {
8607  tempContext = std::make_unique< QgsExpressionContext >();
8608  subContext = tempContext.get();
8609  }
8610 
8612  subContext->appendScope( subScope );
8613 
8614  int limit = 0;
8615  if ( args->count() >= 3 )
8616  {
8617  const QVariant limitVar = args->at( 2 )->eval( parent, context );
8618 
8619  if ( QgsExpressionUtils::isIntSafe( limitVar ) )
8620  {
8621  limit = limitVar.toInt();
8622  }
8623  else
8624  {
8625  return result;
8626  }
8627  }
8628 
8629  for ( const QVariant &value : array )
8630  {
8631  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
8632  if ( args->at( 1 )->eval( parent, subContext ).toBool() )
8633  {
8634  result << value;
8635 
8636  if ( limit > 0 && limit == result.size() )
8637  break;
8638  }
8639  }
8640 
8641  if ( context )
8642  delete subContext->popScope();
8643 
8644  return result;
8645 }
8646 
8647 QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
8648 {
8649  // This is a dummy function, all the real handling is in run
8650  Q_UNUSED( values )
8651  Q_UNUSED( context )
8652  Q_UNUSED( parent )
8653  Q_UNUSED( node )
8654 
8655  Q_ASSERT( false );
8656  return QVariant();
8657 }
8658 
8660 {
8661  QgsExpressionNode::NodeList *args = node->args();
8662 
8663  if ( args->count() < 2 )
8664  // error
8665  return false;
8666 
8667  args->at( 0 )->prepare( parent, context );
8668 
8669  QgsExpressionContext subContext;
8670  if ( context )
8671  subContext = *context;
8672 
8674  subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
8675  subContext.appendScope( subScope );
8676 
8677  args->at( 1 )->prepare( parent, &subContext );
8678 
8679  return true;
8680 }
8682  : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
8683  QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
8684  << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8685  << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
8686  QStringLiteral( "General" ) )
8687 {
8688 
8689 }
8690 
8692 {
8693  bool isStatic = false;
8694 
8695  QgsExpressionNode::NodeList *args = node->args();
8696 
8697  if ( args->count() < 3 )
8698  return false;
8699 
8700  // We only need to check if the node evaluation is static, if both - name and value - are static.
8701  if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
8702  {
8703  QVariant name = args->at( 0 )->eval( parent, context );
8704  QVariant value = args->at( 1 )->eval( parent, context );
8705 
8706  // Temporarily append a new scope to provide the variable
8707  appendTemporaryVariable( context, name.toString(), value );
8708  if ( args->at( 2 )->isStatic( parent, context ) )
8709  isStatic = true;
8710  popTemporaryVariable( context );
8711  }
8712 
8713  return isStatic;
8714 }
8715 
8717 {
8718  Q_UNUSED( node )
8719  QVariant result;
8720 
8721  if ( args->count() < 3 )
8722  // error
8723  return result;
8724 
8725  QVariant name = args->at( 0 )->eval( parent, context );
8726  QVariant value = args->at( 1 )->eval( parent, context );
8727 
8728  const QgsExpressionContext *updatedContext = context;
8729  std::unique_ptr< QgsExpressionContext > tempContext;
8730  if ( !updatedContext )
8731  {
8732  tempContext = std::make_unique< QgsExpressionContext >();
8733  updatedContext = tempContext.get();
8734  }
8735 
8736  appendTemporaryVariable( updatedContext, name.toString(), value );
8737  result = args->at( 2 )->eval( parent, updatedContext );
8738 
8739  if ( context )
8740  popTemporaryVariable( updatedContext );
8741 
8742  return result;
8743 }
8744 
8745 QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
8746 {
8747  // This is a dummy function, all the real handling is in run
8748  Q_UNUSED( values )
8749  Q_UNUSED( context )
8750  Q_UNUSED( parent )
8751  Q_UNUSED( node )
8752 
8753  Q_ASSERT( false );
8754  return QVariant();
8755 }
8756 
8758 {
8759  QgsExpressionNode::NodeList *args = node->args();
8760 
8761  if ( args->count() < 3 )
8762  // error
8763  return false;
8764 
8765  QVariant name = args->at( 0 )->prepare( parent, context );
8766  QVariant value = args->at( 1 )->prepare( parent, context );
8767 
8768  const QgsExpressionContext *updatedContext = context;
8769  std::unique_ptr< QgsExpressionContext > tempContext;
8770  if ( !updatedContext )
8771  {
8772  tempContext = std::make_unique< QgsExpressionContext >();
8773  updatedContext = tempContext.get();
8774  }
8775 
8776  appendTemporaryVariable( updatedContext, name.toString(), value );
8777  args->at( 2 )->prepare( parent, updatedContext );
8778 
8779  if ( context )
8780  popTemporaryVariable( updatedContext );
8781 
8782  return true;
8783 }
8784 
8785 void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
8786 {
8787  QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
8788  delete updatedContext->popScope();
8789 }
8790 
8791 void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
8792 {
8795 
8796  QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
8797  updatedContext->appendScope( scope );
8798 }
QgsCurve
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
QgsGeometry::combine
QgsGeometry combine(const QgsGeometry &geometry) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
Definition: qgsgeometry.cpp:2638
qgspolygon.h
QgsFeatureRequest::NoGeometry
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition: qgsfeaturerequest.h:115
QgsAbstractGeometry::coordinateSequence
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:1052
QgsGeometry::lastError
QString lastError() const SIP_HOLDGIL
Returns an error string referring to the last error encountered either when this geometry was created...
Definition: qgsgeometry.cpp:3308
Qgis::DashPatternLineEndingRule::HalfDash
@ HalfDash
Start or finish the pattern with a half length dash.
QgsExpressionNode::dump
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QgsExpressionContext::hasGeometry
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
Definition: qgsexpressioncontext.cpp:564
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
QgsGeometry::hausdorffDistanceDensify
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
Definition: qgsgeometry.cpp:1945
QgsCoordinateSequence
QVector< QgsRingSequence > QgsCoordinateSequence
Definition: qgsabstractgeometry.h:57
QgsProject::relationManager
QgsRelationManager relationManager
Definition: qgsproject.h:114
QgsMapLayer::crs
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
formatter
Definition: qgsbasicnumericformat.cpp:24
QgsExifTools::getGeoTag
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
Definition: qgsexiftools.cpp:239
QgsColorRamp
Abstract base class for color ramps.
Definition: qgscolorramp.h:29
QgsFeatureRequest::OrderByClause
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Definition: qgsfeaturerequest.h:152
qgsexpressioncontextutils.h
qgsrasterbandstats.h
QgsEllipse
Ellipse geometry type.
Definition: qgsellipse.h:39
QgsAggregateCalculator::AggregateParameters::delimiter
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
Definition: qgsaggregatecalculator.h:110
qgscolorrampimpl.h
QgsAggregateCalculator::Range
@ Range
Range of values (max - min) (numeric and datetime fields only)
Definition: qgsaggregatecalculator.h:79
QgsRectangle::height
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
Qgis::DashPatternLineEndingRule::NoRule
@ NoRule
No special rule.
QgsGeometry::offsetCurve
QgsGeometry offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
Definition: qgsgeometry.cpp:2088
QgsRasterLayer::bandCount
int bandCount() const
Returns the number of bands in this layer.
Definition: qgsrasterlayer.cpp:240
QgsGeometry::squareWaves
QgsGeometry squareWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs square waves along the boundary of the geometry, with the specified wavelength and amplitu...
Definition: qgsgeometry.cpp:1207
QgsSymbolLayerUtils::encodeColor
static QString encodeColor(const QColor &color)
Definition: qgssymbollayerutils.cpp:64
QgsPointXY::y
double y
Definition: qgspointxy.h:63
QgsStaticExpressionFunction::aliases
QStringList aliases() const override
Returns a list of possible aliases for the function.
Definition: qgsexpressionfunction.cpp:191
QgsDistanceArea::measureLength
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
Definition: qgsdistancearea.cpp:217
QgsInterval::seconds
double seconds() const
Returns the interval duration in seconds.
Definition: qgsinterval.h:236
QgsHstoreUtils::build
CORE_EXPORT QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
Definition: qgshstoreutils.cpp:88
QgsGeometry::distance
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
Definition: qgsgeometry.cpp:1915
QgsMapLayer::attributionUrl
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:418
QgsCoordinateTransformContext
Contains information about the context in which a coordinate transform is executed.
Definition: qgscoordinatetransformcontext.h:57
qgsrasterlayer.h
qgsfeaturerequest.h
QgsGeometry::within
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
Definition: qgsgeometry.cpp:1383
QgsGradientColorRamp
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorrampimpl.h:136
QgsFieldFormatter::createCache
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
Definition: qgsfieldformatter.cpp:71
QgsCircle
Circle geometry type.
Definition: qgscircle.h:43
QgsExpressionNodeLiteral
An expression node for literal values.
Definition: qgsexpressionnodeimpl.h:461
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:72
QgsVectorLayer::dataProvider
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Definition: qgsvectorlayer.cpp:676
QgsExpressionContextScope::setVariable
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.
Definition: qgsexpressioncontext.cpp:83
QgsPoint::isValid
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override SIP_HOLDGIL
Checks validity of the geometry, and returns true if the geometry is valid.
Definition: qgspoint.cpp:424
QgsWithVariableExpressionFunction
Handles the with_variable(name, value, node) expression function.
Definition: qgsexpressionfunction.h:579
QgsExpressionContext::popScope
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
Definition: qgsexpressioncontext.cpp:504
QgsGeometry::angleAtVertex
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
Definition: qgsgeometry.cpp:441
QgsMapLayerType::MeshLayer
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
QgsRasterInterface::bandStatistics
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
Definition: qgsrasterinterface.cpp:116
qgslinestring.h
QgsExpressionContext::activeScopeForVariable
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
Definition: qgsexpressioncontext.cpp:356
QgsAbstractGeometry::addZValue
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
QgsEditorWidgetSetup
Holder for the widget type and its configuration for a field.
Definition: qgseditorwidgetsetup.h:28
QgsQuadrilateral
Quadrilateral geometry type.
Definition: qgsquadrilateral.h:36
QgsMapLayerType::VectorLayer
@ VectorLayer
Vector layer.
QgsStaticExpressionFunction::setPrepareFunction
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.
Definition: qgsexpressionfunction.cpp:239
QgsExpressionContextScope::addVariable
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Definition: qgsexpressioncontext.cpp:97
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:48
QgsGeometry::const_parts_end
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
Definition: qgsgeometry.cpp:2026
QgsGeos::maximumInscribedCircle
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition: qgsgeos.cpp:2344
QgsGeometry::transform
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
Definition: qgsgeometry.cpp:3128
QgsProject::mapLayersByName
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
Definition: qgsproject.cpp:3685
qgscoordinateutils.h
sqlite3_database_unique_ptr::prepare
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
Definition: qgssqliteutils.cpp:99
QgsAggregateCalculator::Minority
@ Minority
Minority of values.
Definition: qgsaggregatecalculator.h:80
QgsMeshUtils::centroid
CORE_EXPORT QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
Definition: qgstriangularmesh.cpp:955
algorithm
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
Qgis::DashPatternSizeAdjustment::ScaleGapOnly
@ ScaleGapOnly
Only gap lengths are adjusted.
QgsRasterBandStats
The RasterBandStats struct is a container for statistics about a single raster band.
Definition: qgsrasterbandstats.h:34
QgsAggregateCalculator::AggregateParameters
A bundle of parameters controlling aggregate calculation.
Definition: qgsaggregatecalculator.h:94
QgsCurve::reversed
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
qgsVariantLessThan
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:119
QgsCurvePolygon::exteriorRing
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
Definition: qgscurvepolygon.h:96
QgsAggregateCalculator::CountDistinct
@ CountDistinct
Number of distinct values.
Definition: qgsaggregatecalculator.h:70
QgsExpression::geomCalculator
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Definition: qgsexpression.cpp:401
QgsExpressionFunction::func
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
QgsRectangle::center
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
qgsstringutils.h
QgsVectorLayer::featureCount
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
Definition: qgsvectorlayer.cpp:812
QgsExpressionContext::hasCachedValue
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
Definition: qgsexpressioncontext.cpp:614
QgsCoordinateReferenceSystem::fromOgcWmsCrs
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
Definition: qgscoordinatereferencesystem.cpp:195
QgsAbstractGeometry::addMValue
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
QgsWkbTypes::flatType
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
QgsAggregateCalculator::Min
@ Min
Min of values.
Definition: qgsaggregatecalculator.h:72
Qgis::EndCapStyle
EndCapStyle
End cap styles for buffers.
Definition: qgis.h:966
QgsCurvePolygon
Curve polygon geometry type.
Definition: qgscurvepolygon.h:34
QgsGeometry::interpolate
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
Definition: qgsgeometry.cpp:2489
QgsMapLayerType::AnnotationLayer
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
QgsExpressionNodeColumnRef
An expression node which takes it value from a feature's field.
Definition: qgsexpressionnodeimpl.h:512
QgsMultiPoint::addGeometry
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Definition: qgsmultipoint.cpp:149
QgsQuadrilateral::ConstructionOption
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
Definition: qgsquadrilateral.h:70
QgsStyle::colorRampRef
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance)
Definition: qgsstyle.cpp:468
QgsExpressionContextUtils::registerContextFunctions
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
Definition: qgsexpressioncontextutils.cpp:905
qgssymbollayerutils.h
QgsCoordinateReferenceSystem::description
QString description
Definition: qgscoordinatereferencesystem.h:218
QgsRegularPolygon::CircumscribedCircle
@ CircumscribedCircle
Circumscribed about a circle (the radius is the distance from the center to the midpoints of the side...
Definition: qgsregularpolygon.h:52
QgsFields::count
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:44
QgsRectangle::yMinimum
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
QgsGeometry::fromWkb
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
Definition: qgsgeometry.cpp:344
QgsGeometry::fromWkt
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
QgsGeometry::applyDashPattern
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
Definition: qgsgeometry.cpp:1231
QgsEditorWidgetSetup::config
QVariantMap config() const
Definition: qgseditorwidgetsetup.h:64
QgsArrayFilterExpressionFunction
Handles the array_filter(array, expression) expression function.
Definition: qgsexpressionfunction.h:555
QgsAggregateCalculator::StringConcatenateUnique
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
Definition: qgsaggregatecalculator.h:90
QgsArrayFilterExpressionFunction::prepare
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.
Definition: qgsexpressionfunction.cpp:8659
QgsMultiLineString
Multi line string geometry collection.
Definition: qgsmultilinestring.h:31
QgsRasterBandStats::mean
double mean
The mean cell value for the band. NO_DATA values are excluded.
Definition: qgsrasterbandstats.h:110
QgsExifTools::readTag
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of of an exif tag key stored in the image at imagePath.
Definition: qgsexiftools.cpp:172
QgsGeometry::triangularWaves
QgsGeometry triangularWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs triangular waves along the boundary of the geometry, with the specified wavelength and amp...
Definition: qgsgeometry.cpp:1195
QgsGeometry::centroid
QgsGeometry centroid() const
Returns the center of mass of a geometry.
Definition: qgsgeometry.cpp:2284
QgsAggregateCalculator::CountMissing
@ CountMissing
Number of missing (null) values.
Definition: qgsaggregatecalculator.h:71
qgsmultipoint.h
QgsFeature::geometry
QgsGeometry geometry
Definition: qgsfeature.h:71
qgsfieldformatterregistry.h
QgsPoint::z
double z
Definition: qgspoint.h:71
QgsWkbTypes::geometryDisplayString
static QString geometryDisplayString(GeometryType type) SIP_HOLDGIL
Returns a display string for a geometry type.
Definition: qgswkbtypes.cpp:223
QgsHstoreUtils::parse
CORE_EXPORT QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
Definition: qgshstoreutils.cpp:20
qgis.h
QgsDistanceArea::measureArea
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
Definition: qgsdistancearea.cpp:208
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:480
QgsRelationManager::relation
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Definition: qgsrelationmanager.cpp:95
QgsAbstractGeometry::length
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
Definition: qgsabstractgeometry.cpp:166
QgsRegularPolygon::InscribedCircle
@ InscribedCircle
Inscribed in a circle (the radius is the distance between the center and vertices)
Definition: qgsregularpolygon.h:51
QgsQuadrilateral::Projected
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
Definition: qgsquadrilateral.h:73
QgsRasterBandStats::range
double range
The range is the distance between min & max.
Definition: qgsrasterbandstats.h:113
QgsAggregateCalculator::ArrayAggregate
@ ArrayAggregate
Create an array of values.
Definition: qgsaggregatecalculator.h:89
QgsTransaction::executeSql
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
qgsunittypes.h
QgsVectorDataProvider::transaction
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
Definition: qgsvectordataprovider.cpp:653
QgsMapLayer::abstract
QString abstract() const
Returns the abstract of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:326
QgsAggregateCalculator::Count
@ Count
Count.
Definition: qgsaggregatecalculator.h:69
QgsStringUtils::hammingDistance
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
Definition: qgsstringutils.cpp:283
QgsWithVariableExpressionFunction::func
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
Definition: qgsexpressionfunction.cpp:8745
QgsGeometry::minimalEnclosingCircle
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
Definition: qgsgeometry.cpp:1153
QgsExpressionContextUtils::globalProjectLayerScopes
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Definition: qgsexpressioncontextutils.cpp:377
QgsExpression::BuiltinFunctions
static const QStringList & BuiltinFunctions()
Definition: qgsexpressionfunction.cpp:8462
QgsGeometry::const_parts_begin
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
Definition: qgsgeometry.cpp:2019
QgsAbstractMetadataBase::title
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
Definition: qgsabstractmetadatabase.cpp:51
QgsExpressionNode::NodeList::at
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
Definition: qgsexpressionnode.h:155
QgsTransaction
This class allows including a set of layers in a database-side transaction, provided the layer data p...
Definition: qgstransaction.h:56
QgsInterval::months
double months() const
Returns the interval duration in months (based on a 30 day month).
Definition: qgsinterval.cpp:120
sqlite3_statement_unique_ptr::columnAsInt64
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
Definition: qgssqliteutils.cpp:73
QgsAbstractGeometry::partCount
virtual int partCount() const =0
Returns count of parts contained in the geometry.
fcnRampColor
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction *)
Definition: qgsexpressionfunction.cpp:5205
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
QgsProject::mapLayer
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Definition: qgsproject.cpp:3680
QgsDataProvider::dataSourceUri
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
Definition: qgsdataprovider.h:166
QgsGeometry::overlaps
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
Definition: qgsgeometry.cpp:1371
QgsAggregateCalculator::AggregateParameters::orderBy
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
Definition: qgsaggregatecalculator.h:116
QgsGeometry::roundWaves
QgsGeometry roundWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs rounded (sine-like) waves along the boundary of the geometry, with the specified wavelengt...
Definition: qgsgeometry.cpp:1219
QgsSymbolLayerUtils::decodeColor
static QColor decodeColor(const QString &str)
Definition: qgssymbollayerutils.cpp:69
QgsGeometry::buffer
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...
Definition: qgsgeometry.cpp:2050
QgsExpressionContext::variable
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
Definition: qgsexpressioncontext.cpp:300
sqlite3_database_unique_ptr::errorMessage
QString errorMessage() const
Returns the most recent error message encountered by the database.
Definition: qgssqliteutils.cpp:94
QgsField::name
QString name
Definition: qgsfield.h:60
QgsCurvePolygon::isEmpty
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgscurvepolygon.cpp:1013
Q_GLOBAL_STATIC
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsCurvePolygon::roundness
double roundness() const
Returns the roundness of the curve polygon.
Definition: qgscurvepolygon.cpp:530
QgsExpressionContext::setCachedValue
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
Definition: qgsexpressioncontext.cpp:609
QgsMultiPointXY
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition: qgsgeometry.h:82
QgsWkbTypes::PolygonGeometry
@ PolygonGeometry
Definition: qgswkbtypes.h:144
qgsogcutils.h
QgsMapLayer::dataUrl
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:362
QgsPoint::project
QgsPoint project(double distance, double azimuth, double inclination=90.0) const SIP_HOLDGIL
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition: qgspoint.cpp:735
QgsExpressionContext::fields
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
Definition: qgsexpressioncontext.cpp:595
QgsGeometryUtils::extractLineStrings
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
Definition: qgsgeometryutils.cpp:31
QgsGeometry::mergeLines
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
Definition: qgsgeometry.cpp:2657
QgsArrayForeachExpressionFunction::prepare
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.
Definition: qgsexpressionfunction.cpp:8543
QgsGeometryCollection::numGeometries
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
Definition: qgsgeometrycollection.h:58
QgsRasterBandStats::Range
@ Range
Definition: qgsrasterbandstats.h:68
QgsGeometry::distanceToVertex
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
Definition: qgsgeometry.cpp:425
QgsGeometry::intersection
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
Definition: qgsgeometry.cpp:2616
QgsExpressionFunction::handlesNull
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
Definition: qgsexpressionfunction.cpp:165
sqlite3_statement_unique_ptr::step
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
Definition: qgssqliteutils.cpp:41
QgsFeatureRequest::setFilterFid
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
Definition: qgsfeaturerequest.cpp:141
QgsOgcUtils::Context::transformContext
QgsCoordinateTransformContext transformContext
Definition: qgsogcutils.h:71
QgsMapToPixelSimplifier::Visvalingam
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
Definition: qgsmaptopixelgeometrysimplifier.h:46
QgsMapLayer::providerType
QString providerType() const
Returns the provider type (provider key) for this layer.
Definition: qgsmaplayer.cpp:1864
QgsGeometry::forcePolygonClockwise
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
Definition: qgsgeometry.cpp:2883
QgsRelation::referencingLayer
QgsVectorLayer referencingLayer
Definition: qgsrelation.h:47
QgsFeatureRequest::setFilterExpression
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Definition: qgsfeaturerequest.cpp:167
QgsGeometry::isMultipart
bool isMultipart() const SIP_HOLDGIL
Returns true if WKB of the geometry is of WKBMulti* type.
Definition: qgsgeometry.cpp:389
QgsStyle::defaultStyle
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:145
QgsExpressionFunction::aliases
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
Definition: qgsexpressionfunction.cpp:128
QgsGeometry::nearestPoint
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
Definition: qgsgeometry.cpp:705
QgsQuadrilateral::Distance
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
Definition: qgsquadrilateral.h:72
QgsAbstractGeometry::vertexAt
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
QgsExpressionFunction::referencedColumns
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
Definition: qgsexpressionfunction.cpp:149
QgsRelationManager::relationsByName
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
Definition: qgsrelationmanager.cpp:100
QgsDistanceArea::convertAreaMeasurement
double convertAreaMeasurement(double area, QgsUnitTypes::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Definition: qgsdistancearea.cpp:992
qgsapplication.h
QgsAbstractGeometry::isMeasure
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
Definition: qgsabstractgeometry.h:228
QgsExpression::quotedValue
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
Definition: qgsexpression.cpp:82
QgsFeatureRequest::setFilterRect
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
Definition: qgsfeaturerequest.cpp:101
QgsRasterBandStats::maximumValue
double maximumValue
The maximum cell value in the raster band.
Definition: qgsrasterbandstats.h:101
QgsFeatureRequest::ALL_ATTRIBUTES
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
Definition: qgsfeaturerequest.h:317
QgsWkbTypes::PointM
@ PointM
Definition: qgswkbtypes.h:99
QgsVectorLayer::editorWidgetSetup
QgsEditorWidgetSetup editorWidgetSetup(int index) const
The editor widget setup defines which QgsFieldFormatter and editor widget will be used for the field ...
Definition: qgsvectorlayer.cpp:5779
QgsArrayForeachExpressionFunction::isStatic
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
Definition: qgsexpressionfunction.cpp:8481
QgsCurvePolygon::numInteriorRings
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Definition: qgscurvepolygon.h:86
QgsAggregateCalculator::InterQuartileRange
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only)
Definition: qgsaggregatecalculator.h:84
QgsGeometryCollection::createEmptyWithSameType
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
Definition: qgsgeometrycollection.cpp:112
Qgis::DashPatternLineEndingRule::FullDash
@ FullDash
Start or finish the pattern with a full dash.
QgsExpressionContext::cachedValue
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
Definition: qgsexpressioncontext.cpp:619
QgsCoordinateFormatter::FormatDegreesMinutes
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
Definition: qgscoordinateformatter.h:51
QgsRectangle::xMaximum
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
QgsFeature::id
QgsFeatureId id
Definition: qgsfeature.h:68
QgsEditorWidgetSetup::type
QString type() const
Definition: qgseditorwidgetsetup.h:59
QgsPoint::y
double y
Definition: qgspoint.h:70
QgsExpression::setEvalErrorString
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
Definition: qgsexpression.cpp:388
QgsArrayFilterExpressionFunction::isStatic
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
Definition: qgsexpressionfunction.cpp:8576
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:83
precision
int precision
Definition: qgswfsgetfeature.cpp:103
QgsGeometryCollection
Geometry collection.
Definition: qgsgeometrycollection.h:36
QgsQuadrilateral::squareFromDiagonal
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2) SIP_HOLDGIL
Construct a QgsQuadrilateral as a square from a diagonal.
Definition: qgsquadrilateral.cpp:177
QgsGeometry::poleOfInaccessibility
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
Definition: qgsgeometry.cpp:2323
QgsMapLayer::metadata
QgsLayerMetadata metadata
Definition: qgsmaplayer.h:78
QgsAggregateCalculator::StDevSample
@ StDevSample
Sample standard deviation of values (numeric fields only)
Definition: qgsaggregatecalculator.h:78
qgsgeometryengine.h
QgsExpression::hasEvalError
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
Definition: qgsexpression.cpp:378
QgsGeometry::intersects
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
Definition: qgsgeometry.cpp:1255
QgsQuadrilateral::rectangleFrom3Points
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.
Definition: qgsquadrilateral.cpp:33
QgsCsException
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsWkbTypes::PointZM
@ PointZM
Definition: qgswkbtypes.h:112
QgsLineString::clone
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgslinestring.cpp:296
QgsExpression::unregisterFunction
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
Definition: qgsexpressionfunction.cpp:8440
QgsStaticExpressionFunction::isStatic
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
Definition: qgsexpressionfunction.cpp:212
QgsWithVariableExpressionFunction::run
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...
Definition: qgsexpressionfunction.cpp:8716
QgsGeometryCollection::partCount
int partCount() const override
Returns count of parts contained in the geometry.
Definition: qgsgeometrycollection.cpp:894
QgsArrayForeachExpressionFunction::func
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
Definition: qgsexpressionfunction.cpp:8531
qgshstoreutils.h
QgsExpressionFunction::Parameter
Represents a single parameter passed to a function.
Definition: qgsexpressionfunction.h:54
QgsExpressionFunction::ParameterList
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
Definition: qgsexpressionfunction.h:104
Qgis::DashPatternLineEndingRule::HalfGap
@ HalfGap
Start or finish the pattern with a half length gap.
QgsGeometry::densifyByCount
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
Definition: qgsgeometry.cpp:2263
QgsUnitTypes::encodeUnit
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
Definition: qgsunittypes.cpp:122
QgsMapLayer::dataProvider
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
Definition: qgsmaplayer.cpp:190
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:206
QgsMapLayer::keywordList
QString keywordList() const
Returns the keyword list of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:342
qgsmultipolygon.h
QgsExpressionNode::eval
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
Definition: qgsexpressionnode.cpp:20
QgsFeature::setGeometry
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:170
qgsproviderregistry.h
QgsAbstractMetadataBase::abstract
QString abstract() const
Returns a free-form description of the resource.
Definition: qgsabstractmetadatabase.cpp:61
QgsStaticExpressionFunction::setIsStaticFunction
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.
Definition: qgsexpressionfunction.cpp:228
QgsStaticExpressionFunction::referencedColumns
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
Definition: qgsexpressionfunction.cpp:204
QgsGeometry::extendLine
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...
Definition: qgsgeometry.cpp:2204
QgsExpressionContext::feedback
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
Definition: qgsexpressioncontext.cpp:634
QgsExpressionNodeFunction::args
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
Definition: qgsexpressionnodeimpl.h:433
QgsMapLayerType::GroupLayer
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
QgsGeometry::triangularWavesRandomized
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
Definition: qgsgeometry.cpp:1201
QgsFeature::isValid
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:216
QgsGeometry::smooth
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.
Definition: qgsgeometry.cpp:3484
Qgis::DashPatternSizeAdjustment
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition: qgis.h:1546
qgscolorramp.h
qHash
uint qHash(const QVariant &variant)
Hash for QVariant.
Definition: qgis.cpp:217
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2265
QgsExpressionNode::referencedColumns
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
QgsRasterDataProvider::sample
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.
Definition: qgsrasterdataprovider.cpp:334
QgsGeometry::equals
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
Definition: qgsgeometry.cpp:1340
QgsMapLayer::extent
virtual QgsRectangle extent() const
Returns the extent of the layer.
Definition: qgsmaplayer.cpp:305
QgsGeometryCollection::addGeometry
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
Definition: qgsgeometrycollection.cpp:256
QgsGeometry::variableWidthBufferByM
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Definition: qgsgeometry.cpp:2197
QgsCurvePolygon::area
double area() const override SIP_HOLDGIL
Returns the planar, 2-dimensional area of the geometry.
Definition: qgscurvepolygon.cpp:488
qgsRound
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition: qgis.h:2319
QgsRelation::referencedLayer
QgsVectorLayer referencedLayer
Definition: qgsrelation.h:48
QgsException::what
QString what() const
Definition: qgsexception.h:48
QgsGeometry::taperedBuffer
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
Definition: qgsgeometry.cpp:2190
QgsExpressionFunction::operator==
bool operator==(const QgsExpressionFunction &other) const
Definition: qgsexpressionfunction.cpp:160
QgsAbstractGeometry::simplifiedTypeRef
virtual const QgsAbstractGeometry * simplifiedTypeRef() const SIP_HOLDGIL
Returns a reference to the simplest lossless representation of this geometry, e.g.
Definition: qgsabstractgeometry.cpp:287
QgsCurve::curveSubstring
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
QgsMapLayerType::RasterLayer
@ RasterLayer
Raster layer.
QgsSqliteUtils::quotedValue
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
Definition: qgssqliteutils.cpp:266
QgsVectorLayer::selectedFeatureIds
const Q_INVOKABLE QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
Definition: qgsvectorlayer.cpp:3621
QgsAggregateCalculator::StringMinimumLength
@ StringMinimumLength
Minimum length of string (string fields only)
Definition: qgsaggregatecalculator.h:85
QgsAbstractGeometry::clone
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
QgsRasterBandStats::StdDev
@ StdDev
Definition: qgsrasterbandstats.h:71
QgsGeos
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:103
QgsExpressionNode::referencedVariables
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
qgstriangle.h
qgsexpressionutils.h
QgsGeometry::convexHull
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
Definition: qgsgeometry.cpp:2388
QgsGeometry::rotate
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Definition: qgsgeometry.cpp:910
ENSURE_NO_EVAL_ERROR
#define ENSURE_NO_EVAL_ERROR
Definition: qgsexpressionutils.h:36
QgsLayerMetadata::rights
QStringList rights() const
Returns a list of attribution or copyright strings associated with the resource.
Definition: qgslayermetadata.cpp:51
qgsexpressionfunction.h
QgsGeometry::densifyByDistance
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
Definition: qgsgeometry.cpp:2270
QgsWkbTypes::PointZ
@ PointZ
Definition: qgswkbtypes.h:86
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
QgsCoordinateReferenceSystem::isValid
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Definition: qgscoordinatereferencesystem.cpp:977
QgsWithVariableExpressionFunction::QgsWithVariableExpressionFunction
QgsWithVariableExpressionFunction()
Definition: qgsexpressionfunction.cpp:8681
QgsProviderRegistry::decodeUri
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
Definition: qgsproviderregistry.cpp:555
QgsPoint::m
double m
Definition: qgspoint.h:72
QgsGeometry::length
double length() const
Returns the planar, 2-dimensional length of geometry.
Definition: qgsgeometry.cpp:1890
QgsRasterBandStats::Min
@ Min
Definition: qgsrasterbandstats.h:66
QgsExpressionContextScope::isStatic
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
Definition: qgsexpressioncontext.cpp:168
QgsFeature::attribute
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:327
QgsMapLayer::isEditable
virtual bool isEditable() const
Returns true if the layer can be edited.
Definition: qgsmaplayer.cpp:2021
qgsquadrilateral.h
QgsSpatialIndex
A spatial index for QgsFeature objects.
Definition: qgsspatialindex.h:67
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:169
QgsMapToPixelSimplifier::SimplifyGeometry
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
Definition: qgsmaptopixelgeometrysimplifier.h:57
QgsCoordinateFormatter::formatY
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
Definition: qgscoordinateformatter.cpp:43
QgsGeometry::isEmpty
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
Definition: qgsgeometry.cpp:379
QgsGeometry::extrude
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
Definition: qgsgeometry.cpp:2717
QgsMapLayer::title
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:310
qgsvectorlayerfeatureiterator.h
qgsregularpolygon.h
QgsFeatureRequest::setFeedback
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
Definition: qgsfeaturerequest.cpp:402
QgsArrayForeachExpressionFunction
Handles the array loopingarray_Foreach(array, expression) expression function.
Definition: qgsexpressionfunction.h:532
QgsGeometry::crosses
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
Definition: qgsgeometry.cpp:1395
QgsAbstractGeometry::nCoordinates
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
Definition: qgsabstractgeometry.cpp:150
QgsGeometry::isNull
bool isNull
Definition: qgsgeometry.h:127
QgsWeakMapLayerPointer
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2146
QgsStaticExpressionFunction
c++ helper class for defining QgsExpression functions.
Definition: qgsexpressionfunction.h:349
qgsexiftools.h
QgsCurvePolygon::ringCount
int ringCount(int part=0) const override SIP_HOLDGIL
Returns the number of rings of which this geometry is built.
Definition: qgscurvepolygon.cpp:1254
QgsRectangle::xMinimum
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
QgsAggregateCalculator::Median
@ Median
Median of values (numeric fields only)
Definition: qgsaggregatecalculator.h:76
QgsRegularPolygon::ConstructionOption
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
Definition: qgsregularpolygon.h:49
Qgis::JoinStyle
JoinStyle
Join styles for buffers.
Definition: qgis.h:979
QgsRasterLayer
Represents a raster layer.
Definition: qgsrasterlayer.h:76
QgsAggregateCalculator::Max
@ Max
Max of values.
Definition: qgsaggregatecalculator.h:73
QgsRasterBandStats::Mean
@ Mean
Definition: qgsrasterbandstats.h:70
QgsCurvePolygon::interiorRing
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
Definition: qgscurvepolygon.h:122
QgsExpression::createFieldEqualityExpression
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QVariant::Type fieldType=QVariant::Type::Invalid)
Create an expression allowing to evaluate if a field is equal to a value.
Definition: qgsexpression.cpp:1126
QgsCoordinateReferenceSystem::toProj
QString toProj() const
Returns a Proj string representation of this CRS.
Definition: qgscoordinatereferencesystem.cpp:1293
QgsExpressionNodeFunction
An expression node for expression functions.
Definition: qgsexpressionnodeimpl.h:395
QgsFeatureRequest::setTimeout
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
Definition: qgsfeaturerequest.cpp:385
QgsExpressionFunction::isDeprecated
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
Definition: qgsexpressionfunction.cpp:155
QgsExpression::prepare
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
Definition: qgsexpression.cpp:327
QgsInterval::minutes
double minutes() const
Returns the interval duration in minutes.
Definition: qgsinterval.cpp:187
QgsRingSequence
QVector< QgsPointSequence > QgsRingSequence
Definition: qgsabstractgeometry.h:56
QgsGeometry::pointOnSurface
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
Definition: qgsgeometry.cpp:2308
qgscurvepolygon.h
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:136
QgsMapLayer::minimumScale
double minimumScale() const
Returns the minimum map scale (i.e.
Definition: qgsmaplayer.cpp:904
QgsGeometry::translate
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
Definition: qgsgeometry.cpp:897
QgsGeometry::touches
bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
Definition: qgsgeometry.cpp:1359
QgsMultiPoint
Multi point geometry collection.
Definition: qgsmultipoint.h:29
QgsMapLayer::maximumScale
double maximumScale() const
Returns the maximum map scale (i.e.
Definition: qgsmaplayer.cpp:888
sqlite3_database_unique_ptr::open_v2
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
Definition: qgssqliteutils.cpp:86
QgsAggregateCalculator::StringConcatenate
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
Definition: qgsaggregatecalculator.h:87
QgsRasterBandStats::Sum
@ Sum
Definition: qgsrasterbandstats.h:69
QgsExpression::Functions
static const QList< QgsExpressionFunction * > & Functions()
Definition: qgsexpressionfunction.cpp:7316
qgsexpressionsorter_p.h
Qgis::DashPatternSizeAdjustment::ScaleDashOnly
@ ScaleDashOnly
Only dash lengths are adjusted.
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:211
QgsExpression::evaluate
QVariant evaluate()
Evaluate the feature and return the result.
Definition: qgsexpression.cpp:350
QgsAbstractGeometry
Abstract base class for all geometries.
Definition: qgsabstractgeometry.h:79
QgsExpressionFunction::prepare
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.
Definition: qgsexpressionfunction.cpp:141
QgsExpressionContextScope
Single scope for storing variables and functions for use within a QgsExpressionContext....
Definition: qgsexpressioncontext.h:113
QgsGeometry::simplify
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
Definition: qgsgeometry.cpp:2244
QgsPoint::inclination
double inclination(const QgsPoint &other) const SIP_HOLDGIL
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition: qgspoint.cpp:723
QgsApplication::fieldFormatterRegistry
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Definition: qgsapplication.cpp:2480
qgsstyle.h
qgsgeometryutils.h
QgsAbstractGeometry::is3D
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
Definition: qgsabstractgeometry.h:219
QgsGeometry::singleSidedBuffer
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
Definition: qgsgeometry.cpp:2146
QgsGeometry::vertexAt
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
Definition: qgsgeometry.cpp:683
QgsMapToPixelSimplifier
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
Definition: qgsmaptopixelgeometrysimplifier.h:38
QgsExpressionNode::NodeList::list
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
Definition: qgsexpressionnode.h:148
QgsStaticExpressionFunction::usesGeometry
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
Definition: qgsexpressionfunction.cpp:196
QgsGradientStopsList
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Definition: qgscolorrampimpl.h:122
QgsExpressionContext::appendScope
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Definition: qgsexpressioncontext.cpp:494
qgsvectorlayer.h
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:58
QgsOgcUtils::geometryFromGML
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Definition: qgsogcutils.cpp:188
QgsRasterBandStats::sum
double sum
The sum of all cells in the band. NO_DATA values are excluded.
Definition: qgsrasterbandstats.h:122
QgsRasterBandStats::minimumValue
double minimumValue
The minimum cell value in the raster band.
Definition: qgsrasterbandstats.h:107
Qgis::DashPatternLineEndingRule::FullGap
@ FullGap
Start or finish the pattern with a full gap.
QgsExpressionNode
Abstract base class for all nodes that can appear in an expression.
Definition: qgsexpressionnode.h:34
QgsAggregateCalculator::Mean
@ Mean
Mean of values (numeric fields only)
Definition: qgsaggregatecalculator.h:75
QgsExpression::functionIndex
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
Definition: qgsexpression.cpp:127
QgsGeometry::hausdorffDistance
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
Definition: qgsgeometry.cpp:1933
QgsCurve::clone
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
QgsSpatialIndex::FlagStoreFeatureGeometries
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
Definition: qgsspatialindex.h:77
QgsInterval::weeks
double weeks() const
Returns the interval duration in weeks.
Definition: qgsinterval.cpp:136
QgsOgcUtils::Context::layer
const QgsMapLayer * layer
Definition: qgsogcutils.h:70
QgsGeometry::asPoint
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
Definition: qgsgeometry.cpp:1662
QgsExpression::areaUnits
QgsUnitTypes::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
Definition: qgsexpression.cpp:424
Qgis::DashPatternLineEndingRule
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition: qgis.h:1531
QgsFieldFormatter::representValue
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
Definition: qgsfieldformatter.cpp:24
QgsExpression::distanceUnits
QgsUnitTypes::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
Definition: qgsexpression.cpp:414
QgsRasterBandStats::Max
@ Max
Definition: qgsrasterbandstats.h:67
qgstransaction.h
QgsStringUtils::soundex
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
Definition: qgsstringutils.cpp:322
QgsMapLayer::publicSource
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
Definition: qgsmaplayer.cpp:292
QgsExifTools
Contains utilities for working with EXIF tags in images.
Definition: qgsexiftools.h:32
QgsMapLayer::source
QString source() const
Returns the source for the layer.
Definition: qgsmaplayer.cpp:300
QgsExpressionNode::isStatic
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
QgsCoordinateFormatter::FormatDegreesMinutesSeconds
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
Definition: qgscoordinateformatter.h:50
QgsGeometry::get
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:141
QgsGeometry::createGeometryEngine
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
Definition: qgsgeometry.cpp:3972
QgsAggregateCalculator::ThirdQuartile
@ ThirdQuartile
Third quartile (numeric fields only)
Definition: qgsaggregatecalculator.h:83
QgsWkbTypes::LineGeometry
@ LineGeometry
Definition: qgswkbtypes.h:143
QgsExpressionUtils
A set of expression-related functions.
Definition: qgsexpressionutils.h:49
QgsCoordinateFormatter::FlagDegreesUseStringSuffix
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
Definition: qgscoordinateformatter.h:60
QgsGeometry::asWkt
QString asWkt(int precision=17) const
Exports the geometry to WKT.
Definition: qgsgeometry.cpp:1407
QgsWkbTypes::PointGeometry
@ PointGeometry
Definition: qgswkbtypes.h:142
QgsGeometry::interpolateAngle
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
Definition: qgsgeometry.cpp:2558
QgsExpressionFunction::usesGeometry
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
Definition: qgsexpressionfunction.cpp:122
QgsCoordinateReferenceSystem::mapUnits
QgsUnitTypes::DistanceUnit mapUnits
Definition: qgscoordinatereferencesystem.h:215
QgsFeatureRequest::setRequestMayBeNested
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
Definition: qgsfeaturerequest.cpp:396
QgsRectangle::yMaximum
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
QgsGeometryCollection::geometryN
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
Definition: qgsgeometrycollection.h:86
QgsVectorLayer::mapTipTemplate
QString mapTipTemplate
Definition: qgsvectorlayer.h:397
QgsExpressionFunction::lazyEval
bool lazyEval() const
true if this function should use lazy evaluation.
Definition: qgsexpressionfunction.h:232
ExpressionFunctionList
QList< QgsExpressionFunction * > ExpressionFunctionList
Definition: qgsexpressionfunction.cpp:74
QgsAggregateCalculator::Aggregate
Aggregate
Available aggregates to calculate.
Definition: qgsaggregatecalculator.h:67
qgsthreadingutils.h
qgsexpressionnodeimpl.h
QgsFeatureRequest::OrderBy
Represents a list of OrderByClauses, with the most important first and the least important last.
Definition: qgsfeaturerequest.h:264
QgsExpressionNodeColumnRef::name
QString name() const
The name of the column.
Definition: qgsexpressionnodeimpl.h:534
QgsSpatialIndex::nearestNeighbor
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
Definition: qgsspatialindex.cpp:505
QgsGeometry::shortestLine
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Definition: qgsgeometry.cpp:720
QgsExpressionFunction::run
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...
Definition: qgsexpressionfunction.cpp:89
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:399
QgsDistanceArea::measurePerimeter
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
Definition: qgsdistancearea.cpp:226
QgsPointSequence
QVector< QgsPoint > QgsPointSequence
Definition: qgsabstractgeometry.h:52
qgscurve.h
c
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
Definition: porting_processing.dox:1
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsExpressionFunction
A abstract base class for defining QgsExpression functions.
Definition: qgsexpressionfunction.h:40
QgsThreadingUtils::runOnMainThread
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
Definition: qgsthreadingutils.h:56
QgsAggregateCalculator::Majority
@ Majority
Majority of values.
Definition: qgsaggregatecalculator.h:81
QgsStaticExpressionFunction::prepare
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.
Definition: qgsexpressionfunction.cpp:220
str
#define str(x)
Definition: qgis.cpp:37
QgsRegularPolygon
Regular Polygon geometry type.
Definition: qgsregularpolygon.h:41
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsRectangle::width
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
QgsAbstractGeometry::parts_end
part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
Definition: qgsabstractgeometry.cpp:302
QgsFeatureRequest::setLimit
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
Definition: qgsfeaturerequest.cpp:216
QgsFeatureSource::materialize
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...
Definition: qgsfeaturesource.cpp:132
QgsDistanceArea::convertLengthMeasurement
double convertLengthMeasurement(double length, QgsUnitTypes::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
Definition: qgsdistancearea.cpp:978
QgsArrayForeachExpressionFunction::run
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...
Definition: qgsexpressionfunction.cpp:8497
QgsMapLayer
Base class for all map layer types. This is the base class for all map layer types (vector,...
Definition: qgsmaplayer.h:72
QgsCoordinateFormatter::Format
Format
Available formats for displaying coordinates.
Definition: qgscoordinateformatter.h:47
sqlite3_database_unique_ptr::exec
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Definition: qgssqliteutils.cpp:109
QgsExpressionContext::geometry
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
Definition: qgsexpressioncontext.cpp:574
Qgis::GeometryOperationResult::Success
@ Success
Operation succeeded.
QgsRelation::isValid
bool isValid
Definition: qgsrelation.h:50
QgsPointXY::x
double x
Definition: qgspointxy.h:62
QgsExpressionContext::feature
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
Definition: qgsexpressioncontext.cpp:543
QgsInterval
A representation of the interval between two datetime values.
Definition: qgsinterval.h:41
QgsExpressionFunction::name
QString name() const
The name of the function.
Definition: qgsexpressionfunction.h:190
QgsGeometry::fromRect
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
Definition: qgsgeometry.cpp:241
QgsVertexId
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:30
QgsInterval::years
double years() const
Returns the interval duration in years (based on an average year length)
Definition: qgsinterval.cpp:104
QgsFieldFormatterRegistry::fieldFormatter
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
Definition: qgsfieldformatterregistry.cpp:76
qgsmaptopixelgeometrysimplifier.h
QgsExpression::helpText
static QString helpText(QString name)
Returns the help text for a specified function.
Definition: qgsexpression.cpp:546
QgsGeometry::forceRHR
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
Definition: qgsgeometry.cpp:2878
QgsColorRamp::color
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
QgsMapLayerType::VectorTileLayer
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
Q_DECLARE_METATYPE
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:76
QgsStringUtils::wordWrap
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.
Definition: qgsstringutils.cpp:602
QgsAggregateCalculator::stringToAggregate
static Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
Definition: qgsaggregatecalculator.cpp:188
QgsRelation
Definition: qgsrelation.h:42
QgsGeometry::asMultiPoint
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
Definition: qgsgeometry.cpp:1759
QgsRasterBandStats::stdDev
double stdDev
The standard deviation of the cell values.
Definition: qgsrasterbandstats.h:116
QgsCoordinateReferenceSystem::authid
QString authid
Definition: qgscoordinatereferencesystem.h:217
QgsExpression::needsGeometry
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
Definition: qgsexpression.cpp:270
QgsAbstractGeometry::boundary
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
QgsVectorLayer::storageType
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
Definition: qgsvectorlayer.cpp:369
QgsInterval::days
double days() const
Returns the interval duration in days.
Definition: qgsinterval.cpp:153
QgsGeometry::createWedgeBuffer
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
Definition: qgsgeometry.cpp:288
QgsExpressionFunction::allParamsStatic
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...
Definition: qgsexpressionfunction.cpp:244
QgsGeometry::squareWavesRandomized
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
Definition: qgsgeometry.cpp:1213
QgsAbstractMetadataBase::KeywordMap
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
Definition: qgsabstractmetadatabase.h:78
QgsCurve::straightDistance2d
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition: qgscurve.cpp:272
QgsGeometry::forcePolygonCounterClockwise
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
Definition: qgsgeometry.cpp:2925
QgsAggregateCalculator::FirstQuartile
@ FirstQuartile
First quartile (numeric fields only)
Definition: qgsaggregatecalculator.h:82
FEAT_FROM_CONTEXT
#define FEAT_FROM_CONTEXT(c, f)
Definition: qgsexpressionutils.h:39
QgsSpatialIndex::intersects
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
Definition: qgsspatialindex.cpp:492
QgsArrayFilterExpressionFunction::run
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...
Definition: qgsexpressionfunction.cpp:8592
QgsGeometry::boundingBox
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Definition: qgsgeometry.cpp:1080
QgsExpression::registerFunction
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
Definition: qgsexpressionfunction.cpp:8427
QgsMultiLineString::addGeometry
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Definition: qgsmultilinestring.cpp:131
QgsDistanceArea
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
Definition: qgsdistancearea.h:52
qgsspatialindex.h
QgsFeature::fields
QgsFields fields
Definition: qgsfeature.h:70
QgsInterval::hours
double hours() const
Returns the interval duration in hours.
Definition: qgsinterval.cpp:170
QgsGeometry::vertexIdFromVertexNr
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
Definition: qgsgeometry.cpp:3263
qgsexception.h
QgsAggregateCalculator::Sum
@ Sum
Sum of values.
Definition: qgsaggregatecalculator.h:74
qgsdistancearea.h
QgsArrayForeachExpressionFunction::QgsArrayForeachExpressionFunction
QgsArrayForeachExpressionFunction()
Definition: qgsexpressionfunction.cpp:8472
QgsAggregateCalculator::AggregateParameters::filter
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
Definition: qgsaggregatecalculator.h:103
QgsAggregateCalculator::GeometryCollect
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
Definition: qgsaggregatecalculator.h:88
QgsCoordinateFormatter::FlagDegreesPadMinutesSeconds
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
Definition: qgscoordinateformatter.h:61
QgsFeature
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:55
segment
QLineF segment(int index, QRectF rect, double radius)
Definition: qgsshapegenerator.cpp:25
QgsExpressionFunction::isStatic
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
Definition: qgsexpressionfunction.cpp:133
QgsArrayFilterExpressionFunction::func
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
Definition: qgsexpressionfunction.cpp:8647
QgsStringUtils::levenshteinDistance
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
Definition: qgsstringutils.cpp:138
QgsGeometry::symDifference
QgsGeometry symDifference(const QgsGeometry &geometry) const
Returns a geometry representing the points making up this geometry that do not make up other.
Definition: qgsgeometry.cpp:2697
QgsVectorLayer::aggregate
QVariant aggregate(QgsAggregateCalculator::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
Definition: qgsvectorlayer.cpp:4543
QgsGeometry::contains
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
Definition: qgsgeometry.cpp:1303
ENSURE_GEOM_TYPE
#define ENSURE_GEOM_TYPE(f, g, geomtype)
Definition: qgsexpressionfunction.cpp:2489
RelationFunction
bool(QgsGeometry::* RelationFunction)(const QgsGeometry &geometry) const
Definition: qgsexpressionfunction.cpp:6733
QgsGeometry::vertices_end
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
Definition: qgsgeometry.cpp:1989
QgsFeatureRequest::setDestinationCrs
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
Definition: qgsfeaturerequest.cpp:301
QgsRelation::getRelatedFeaturesFilter
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
Definition: qgsrelation.cpp:203
QgsVectorLayer::getFeature
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
Definition: qgsvectorlayer.h:1187
QgsRectangle::grow
void grow(double delta)
Grows the rectangle in place by the specified amount.
Definition: qgsrectangle.h:296
QgsVectorLayer::selectedFeatureCount
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Definition: qgsvectorlayer.cpp:3616
QgsAbstractGeometry::parts_begin
part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of the geometry.
Definition: qgsabstractgeometry.h:835
QgsCurve::segmentize
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:175
QgsGeometry::vertices_begin
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
Definition: qgsgeometry.cpp:1982
QgsCurve::isClosed
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:53
QgsFields::lookupField
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
QgsCoordinateFormatter::formatX
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
Definition: qgscoordinateformatter.cpp:24
QgsExpressionContextScope::StaticVariable
Single variable definition for use within a QgsExpressionContextScope.
Definition: qgsexpressioncontext.h:120
QgsField::editorWidgetSetup
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:602
QgsFields::at
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
QgsGeometry::orientedMinimumBoundingBox
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...
Definition: qgsgeometry.cpp:1089
QgsExpression::cleanRegisteredFunctions
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Definition: qgsexpressionfunction.cpp:8456
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
qgscoordinateformatter.h
QgsVectorLayer::displayExpression
QString displayExpression
Definition: qgsvectorlayer.h:396
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:57
QgsArrayFilterExpressionFunction::QgsArrayFilterExpressionFunction
QgsArrayFilterExpressionFunction()
Definition: qgsexpressionfunction.cpp:8566
sqlite3_database_unique_ptr
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
Definition: qgssqliteutils.h:118
QgsGeometry::type
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:128
QgsExpressionNode::prepare
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
Definition: qgsexpressionnode.cpp:37
QgsWithVariableExpressionFunction::prepare
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.
Definition: qgsexpressionfunction.cpp:8757
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:289
QgsVectorLayer::geometryType
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Definition: qgsvectorlayer.cpp:720
QgsMapLayerType::PointCloudLayer
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
QgsCurve::sinuosity
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition: qgscurve.cpp:277
QgsProviderRegistry::instance
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Definition: qgsproviderregistry.cpp:73
MathUtils::angle
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
QgsGeometry::area
double area() const
Returns the planar, 2-dimensional area of the geometry.
Definition: qgsgeometry.cpp:1880
QgsAbstractMetadataBase::keywords
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource.
Definition: qgsabstractmetadatabase.cpp:86
QgsExpression::replaceExpressionText
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...
Definition: qgsexpression.cpp:434
QgsGeometryCollection::removeGeometry
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
Definition: qgsgeometrycollection.cpp:282
QgsExpressionNode::NodeList::count
int count() const
Returns the number of nodes in the list.
Definition: qgsexpressionnode.h:137
QgsRegularPolygon::toPolygon
QgsPolygon * toPolygon() const
Returns as a polygon.
Definition: qgsregularpolygon.cpp:183
QgsFieldFormatter
A field formatter helps to handle and display values for a field.
Definition: qgsfieldformatter.h:72
QgsFeatureRequest::setFlags
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
Definition: qgsfeaturerequest.cpp:222
QgsSqliteUtils::quotedIdentifier
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
Definition: qgssqliteutils.cpp:259
QgsAggregateCalculator::StringMaximumLength
@ StringMaximumLength
Maximum length of string (string fields only)
Definition: qgsaggregatecalculator.h:86
QgsStaticExpressionFunction::setIsStatic
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
Definition: qgsexpressionfunction.cpp:233
QgsStaticExpressionFunction::QgsStaticExpressionFunction
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...
Definition: qgsexpressionfunction.h:356
QgsInterval::isValid
bool isValid() const
Returns true if the interval is valid.
Definition: qgsinterval.h:255
QgsPoint::x
double x
Definition: qgspoint.h:69
QgsGradientStop
Represents a color stop within a QgsGradientColorRamp color ramp.
Definition: qgscolorrampimpl.h:38
QgsGeometry::difference
QgsGeometry difference(const QgsGeometry &geometry) const
Returns a geometry representing the points making up this geometry that do not make up other.
Definition: qgsgeometry.cpp:2677
QgsMapLayerType::PluginLayer
@ PluginLayer
Plugin based layer.
sqlite3_statement_unique_ptr
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
Definition: qgssqliteutils.h:69
QgsRasterLayer::dataProvider
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Definition: qgsrasterlayer.cpp:257
QgsSurface::boundingBox
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition: qgssurface.h:43
qgsmultilinestring.h
qgsfieldformatter.h
QgsOgcUtils::Context
The Context struct stores the current layer and coordinate transform context.
Definition: qgsogcutils.h:59
qgsgeos.h
QgsTriangle
Triangle geometry type.
Definition: qgstriangle.h:33
Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
QgsExpressionNode::NodeList
A list of expression nodes.
Definition: qgsexpressionnode.h:121
QgsGeometry::collectGeometry
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
Definition: qgsgeometry.cpp:259
QgsGeometry::roundWavesRandomized
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
Definition: qgsgeometry.cpp:1225
QgsExpressionContext::hasFeature
bool hasFeature() const
Returns true if the context has a feature associated with it.
Definition: qgsexpressioncontext.cpp:533
QgsFeatureId
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
qgsmessagelog.h
QgsQuadrilateral::toPolygon
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
Definition: qgsquadrilateral.cpp:386
QgsGeometry::isGeosValid
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
Definition: qgsgeometry.cpp:3019
QgsExpressionContext::setFeature
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Definition: qgsexpressioncontext.cpp:525
QgsStringUtils::longestCommonSubstring
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
Definition: qgsstringutils.cpp:220
QgsWithVariableExpressionFunction::isStatic
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
Definition: qgsexpressionfunction.cpp:8691
QgsGeometry::lineLocatePoint
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
Definition: qgsgeometry.cpp:2539
QgsGeometry::disjoint
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
Definition: qgsgeometry.cpp:1328
QgsMapLayer::type
QgsMapLayerType type
Definition: qgsmaplayer.h:80