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