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