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