QGIS API Documentation 3.43.0-Master (3ee7834ace6)
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 : matthias@opengis.ch
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
20#include "qgscoordinateutils.h"
22#include "qgsexpressionutils.h"
24#include "qgsexiftools.h"
25#include "qgsfeaturerequest.h"
26#include "qgsgeos.h"
27#include "qgsstringutils.h"
28#include "qgsmultipoint.h"
29#include "qgsgeometryutils.h"
30#include "qgshstoreutils.h"
31#include "qgsmultilinestring.h"
32#include "qgslinestring.h"
33#include "qgscurvepolygon.h"
35#include "qgspolygon.h"
36#include "qgstriangle.h"
37#include "qgscurve.h"
38#include "qgsregularpolygon.h"
39#include "qgsquadrilateral.h"
40#include "qgsvariantutils.h"
41#include "qgsogcutils.h"
42#include "qgsdistancearea.h"
43#include "qgsgeometryengine.h"
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 "qgsvectorlayerutils.h"
52#include "qgsrasterbandstats.h"
53#include "qgscolorramp.h"
55#include "qgsfieldformatter.h"
57#include "qgsproviderregistry.h"
58#include "sqlite3.h"
59#include "qgstransaction.h"
60#include "qgsthreadingutils.h"
61#include "qgsapplication.h"
62#include "qgis.h"
64#include "qgsunittypes.h"
65#include "qgsspatialindex.h"
66#include "qgscolorrampimpl.h"
67
68#include <QMimeDatabase>
69#include <QProcessEnvironment>
70#include <QCryptographicHash>
71#include <QRegularExpression>
72#include <QUuid>
73#include <QUrlQuery>
74
75typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
76
78Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
80
83Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
84
85const QString QgsExpressionFunction::helpText() const
86{
87 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
88}
89
91{
92 Q_UNUSED( node )
93 // evaluate arguments
94 QVariantList argValues;
95 if ( args )
96 {
97 int arg = 0;
98 const QList< QgsExpressionNode * > argList = args->list();
99 for ( QgsExpressionNode *n : argList )
100 {
101 QVariant v;
102 if ( lazyEval() )
103 {
104 // Pass in the node for the function to eval as it needs.
105 v = QVariant::fromValue( n );
106 }
107 else
108 {
109 v = n->eval( parent, context );
111 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
112 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
113 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
114 }
115 argValues.append( v );
116 arg++;
117 }
118 }
119
120 return func( argValues, context, parent, node );
121}
122
124{
125 Q_UNUSED( node )
126 return true;
127}
128
130{
131 return QStringList();
132}
133
135{
136 Q_UNUSED( parent )
137 Q_UNUSED( context )
138 Q_UNUSED( node )
139 return false;
140}
141
143{
144 Q_UNUSED( parent )
145 Q_UNUSED( context )
146 Q_UNUSED( node )
147 return true;
148}
149
151{
152 Q_UNUSED( node )
153 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
154}
155
157{
158 return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
159}
160
162{
163 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
164}
165
167{
168 return mHandlesNull;
169}
170
171// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
174 FcnEval fcn,
175 const QString &group,
176 const QString &helpText,
177 const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
178 const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
179 bool lazyEval,
180 const QStringList &aliases,
181 bool handlesNull )
182 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
183 , mFnc( fcn )
184 , mAliases( aliases )
185 , mUsesGeometry( false )
186 , mUsesGeometryFunc( usesGeometry )
187 , mReferencedColumnsFunc( referencedColumns )
188{
189}
191
193{
194 return mAliases;
195}
196
198{
199 if ( mUsesGeometryFunc )
200 return mUsesGeometryFunc( node );
201 else
202 return mUsesGeometry;
203}
204
205void QgsStaticExpressionFunction::setUsesGeometryFunction( const std::function<bool ( const QgsExpressionNodeFunction * )> &usesGeometry )
206{
207 mUsesGeometryFunc = usesGeometry;
208}
209
211{
212 if ( mReferencedColumnsFunc )
213 return mReferencedColumnsFunc( node );
214 else
215 return mReferencedColumns;
216}
217
219{
220 if ( mIsStaticFunc )
221 return mIsStaticFunc( node, parent, context );
222 else
223 return mIsStatic;
224}
225
227{
228 if ( mPrepareFunc )
229 return mPrepareFunc( node, parent, context );
230
231 return true;
232}
233
235{
236 mIsStaticFunc = isStatic;
237}
238
240{
241 mIsStaticFunc = nullptr;
242 mIsStatic = isStatic;
243}
244
245void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
246{
247 mPrepareFunc = prepareFunc;
248}
249
251{
252 if ( node && node->args() )
253 {
254 const QList< QgsExpressionNode * > argList = node->args()->list();
255 for ( QgsExpressionNode *argNode : argList )
256 {
257 if ( !argNode->isStatic( parent, context ) )
258 return false;
259 }
260 }
261
262 return true;
263}
264
265static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
266{
267 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
268 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
269 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
270
271 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
272 return QVariant();
273
274 QVariantList array;
275 int length = 1;
276
277 array << start;
278 double current = start + step;
279 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
280 {
281 array << current;
282 current += step;
283 length++;
284 }
285
286 return array;
287}
288
289static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
290{
291 if ( !context )
292 return QVariant();
293
294 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
295
296 if ( name == QLatin1String( "feature" ) )
297 {
298 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
299 }
300 else if ( name == QLatin1String( "id" ) )
301 {
302 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
303 }
304 else if ( name == QLatin1String( "geometry" ) )
305 {
306 if ( !context->hasFeature() )
307 return QVariant();
308
309 const QgsFeature feature = context->feature();
310 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
311 }
312 else
313 {
314 return context->variable( name );
315 }
316}
317
318static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
319{
320 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
321 return QgsExpression::replaceExpressionText( templateString, context );
322}
323
324static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
325{
326 if ( !context )
327 return QVariant();
328
329 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
330 QgsExpression expression( expString );
331 return expression.evaluate( context );
332}
333
334static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
335{
336 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
337 return QVariant( std::sqrt( x ) );
338}
339
340static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
341{
342 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
343 return QVariant( std::fabs( val ) );
344}
345
346static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
347{
348 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
349 return ( deg * M_PI ) / 180;
350}
351static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
352{
353 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
354 return ( 180 * rad ) / M_PI;
355}
356static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
357{
358 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
359 return QVariant( std::sin( x ) );
360}
361static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
362{
363 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
364 return QVariant( std::cos( x ) );
365}
366static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
367{
368 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
369 return QVariant( std::tan( x ) );
370}
371static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
372{
373 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
374 return QVariant( std::asin( x ) );
375}
376static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
377{
378 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
379 return QVariant( std::acos( x ) );
380}
381static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
382{
383 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
384 return QVariant( std::atan( x ) );
385}
386static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
387{
388 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
389 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
390 return QVariant( std::atan2( y, x ) );
391}
392static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
393{
394 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
395 return QVariant( std::exp( x ) );
396}
397static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
398{
399 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
400 if ( x <= 0 )
401 return QVariant();
402 return QVariant( std::log( x ) );
403}
404static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
405{
406 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
407 if ( x <= 0 )
408 return QVariant();
409 return QVariant( log10( x ) );
410}
411static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
412{
413 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
414 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
415 if ( x <= 0 || b <= 0 )
416 return QVariant();
417 return QVariant( std::log( x ) / std::log( b ) );
418}
419static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
420{
421 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
422 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
423 if ( max < min )
424 return QVariant();
425
426 std::random_device rd;
427 std::mt19937_64 generator( rd() );
428
429 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
430 {
431 quint32 seed;
432 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
433 {
434 // if seed can be converted to int, we use as is
435 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
436 }
437 else
438 {
439 // if not, we hash string representation to int
440 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
441 std::hash<std::string> hasher;
442 seed = hasher( seedStr.toStdString() );
443 }
444 generator.seed( seed );
445 }
446
447 // Return a random double in the range [min, max] (inclusive)
448 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
449 return QVariant( min + f * ( max - min ) );
450}
451static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
452{
453 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
454 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
455 if ( max < min )
456 return QVariant();
457
458 std::random_device rd;
459 std::mt19937_64 generator( rd() );
460
461 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
462 {
463 quint32 seed;
464 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
465 {
466 // if seed can be converted to int, we use as is
467 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
468 }
469 else
470 {
471 // if not, we hash string representation to int
472 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
473 std::hash<std::string> hasher;
474 seed = hasher( seedStr.toStdString() );
475 }
476 generator.seed( seed );
477 }
478
479 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
480 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
481 return QVariant( randomInteger );
482
483 // Prevent wrong conversion of QVariant. See #36412
484 return QVariant( int( randomInteger ) );
485}
486
487static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
488{
489 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
490 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
491 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
492 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
493 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
494
495 if ( domainMin >= domainMax )
496 {
497 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
498 return QVariant();
499 }
500
501 // outside of domain?
502 if ( val >= domainMax )
503 {
504 return rangeMax;
505 }
506 else if ( val <= domainMin )
507 {
508 return rangeMin;
509 }
510
511 // calculate linear scale
512 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
513 double c = rangeMin - ( domainMin * m );
514
515 // Return linearly scaled value
516 return QVariant( m * val + c );
517}
518
519static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
520{
521 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
522 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
523 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
524 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
525 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
526 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
527
528 if ( domainMin >= domainMax )
529 {
530 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
531 return QVariant();
532 }
533 if ( exponent <= 0 )
534 {
535 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
536 return QVariant();
537 }
538
539 // outside of domain?
540 if ( val >= domainMax )
541 {
542 return rangeMax;
543 }
544 else if ( val <= domainMin )
545 {
546 return rangeMin;
547 }
548
549 // Return polynomially scaled value
550 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
551}
552
553static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
554{
555 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
556 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
557 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
558 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
559 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
560 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
561
562 if ( domainMin >= domainMax )
563 {
564 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
565 return QVariant();
566 }
567 if ( exponent <= 0 )
568 {
569 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
570 return QVariant();
571 }
572
573 // outside of domain?
574 if ( val >= domainMax )
575 {
576 return rangeMax;
577 }
578 else if ( val <= domainMin )
579 {
580 return rangeMin;
581 }
582
583 // Return exponentially scaled value
584 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
585 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
586}
587
588static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
589{
590 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
591 double maxVal = std::numeric_limits<double>::quiet_NaN();
592 for ( const QVariant &val : values )
593 {
594 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
595 if ( std::isnan( maxVal ) )
596 {
597 maxVal = testVal;
598 }
599 else if ( !std::isnan( testVal ) )
600 {
601 maxVal = std::max( maxVal, testVal );
602 }
603 }
604
605 if ( !std::isnan( maxVal ) )
606 {
607 result = QVariant( maxVal );
608 }
609 return result;
610}
611
612static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
613{
614 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
615 double minVal = std::numeric_limits<double>::quiet_NaN();
616 for ( const QVariant &val : values )
617 {
618 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
619 if ( std::isnan( minVal ) )
620 {
621 minVal = testVal;
622 }
623 else if ( !std::isnan( testVal ) )
624 {
625 minVal = std::min( minVal, testVal );
626 }
627 }
628
629 if ( !std::isnan( minVal ) )
630 {
631 result = QVariant( minVal );
632 }
633 return result;
634}
635
636static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
637{
638 //lazy eval, so we need to evaluate nodes now
639
640 //first node is layer id or name
641 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
643 QVariant value = node->eval( parent, context );
645
646 // TODO this expression function is NOT thread safe
648 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
650 if ( !vl )
651 {
652 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
653 return QVariant();
654 }
655
656 // second node is aggregate type
657 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
659 value = node->eval( parent, context );
661 bool ok = false;
662 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
663 if ( !ok )
664 {
665 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
666 return QVariant();
667 }
668
669 // third node is subexpression (or field name)
670 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
672 QString subExpression = node->dump();
673
675 //optional forth node is filter
676 if ( values.count() > 3 )
677 {
678 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
680 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
681 if ( !nl || nl->value().isValid() )
682 parameters.filter = node->dump();
683 }
684
685 //optional fifth node is concatenator
686 if ( values.count() > 4 )
687 {
688 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
690 value = node->eval( parent, context );
692 parameters.delimiter = value.toString();
693 }
694
695 //optional sixth node is order by
696 QString orderBy;
697 if ( values.count() > 5 )
698 {
699 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
701 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
702 if ( !nl || nl->value().isValid() )
703 {
704 orderBy = node->dump();
705 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
706 }
707 }
708
709 QString aggregateError;
710 QVariant result;
711 if ( context )
712 {
713 QString cacheKey;
714 QgsExpression subExp( subExpression );
715 QgsExpression filterExp( parameters.filter );
716
717 const QSet< QString > filterVars = filterExp.referencedVariables();
718 const QSet< QString > subExpVars = subExp.referencedVariables();
719 QSet<QString> allVars = filterVars + subExpVars;
720
721 bool isStatic = true;
722 if ( filterVars.contains( QStringLiteral( "parent" ) )
723 || filterVars.contains( QString() )
724 || subExpVars.contains( QStringLiteral( "parent" ) )
725 || subExpVars.contains( QString() ) )
726 {
727 isStatic = false;
728 }
729 else
730 {
731 for ( const QString &varName : allVars )
732 {
733 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
734 if ( scope && !scope->isStatic( varName ) )
735 {
736 isStatic = false;
737 break;
738 }
739 }
740 }
741
742 if ( isStatic && ! parameters.orderBy.isEmpty() )
743 {
744 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
745 {
746 const QgsExpression &orderByExpression { orderByClause.expression() };
747 if ( orderByExpression.referencedVariables().contains( QStringLiteral( "parent" ) ) || orderByExpression.referencedVariables().contains( QString() ) )
748 {
749 isStatic = false;
750 break;
751 }
752 }
753 }
754
755 if ( !isStatic )
756 {
757 bool ok = false;
758 const QString contextHash = context->uniqueHash( ok, allVars );
759 if ( ok )
760 {
761 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
762 orderBy, contextHash );
763 }
764 }
765 else
766 {
767 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
768 }
769
770 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
771 {
772 return context->cachedValue( cacheKey );
773 }
774
775 QgsExpressionContext subContext( *context );
777 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
778 subContext.appendScope( subScope );
779 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
780
781 if ( ok && !cacheKey.isEmpty() )
782 {
783 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
784 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
785 // associated with it's calculation!
786 context->setCachedValue( cacheKey, result );
787 }
788 }
789 else
790 {
791 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
792 }
793 if ( !ok )
794 {
795 if ( !aggregateError.isEmpty() )
796 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
797 else
798 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
799 return QVariant();
800 }
801
802 return result;
803}
804
805static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
806{
807 if ( !context )
808 {
809 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
810 return QVariant();
811 }
812
813 // first step - find current layer
814
815 // TODO this expression function is NOT thread safe
817 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
819 if ( !vl )
820 {
821 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
822 return QVariant();
823 }
824
825 //lazy eval, so we need to evaluate nodes now
826
827 //first node is relation name
828 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
830 QVariant value = node->eval( parent, context );
832 QString relationId = value.toString();
833 // check relation exists
834 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
835 if ( !relation.isValid() || relation.referencedLayer() != vl )
836 {
837 // check for relations by name
838 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
839 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
840 {
841 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
842 return QVariant();
843 }
844 else
845 {
846 relation = relations.at( 0 );
847 }
848 }
849
850 QgsVectorLayer *childLayer = relation.referencingLayer();
851
852 // second node is aggregate type
853 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
855 value = node->eval( parent, context );
857 bool ok = false;
858 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
859 if ( !ok )
860 {
861 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
862 return QVariant();
863 }
864
865 //third node is subexpression (or field name)
866 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
868 QString subExpression = node->dump();
869
870 //optional fourth node is concatenator
872 if ( values.count() > 3 )
873 {
874 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
876 value = node->eval( parent, context );
878 parameters.delimiter = value.toString();
879 }
880
881 //optional fifth node is order by
882 QString orderBy;
883 if ( values.count() > 4 )
884 {
885 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
887 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
888 if ( !nl || nl->value().isValid() )
889 {
890 orderBy = node->dump();
891 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
892 }
893 }
894
895 if ( !context->hasFeature() )
896 return QVariant();
897 QgsFeature f = context->feature();
898
899 parameters.filter = relation.getRelatedFeaturesFilter( f );
900
901 const QString cacheKey = QStringLiteral( "relagg:%1%:%2:%3:%4:%5:%6" ).arg( relationId, vl->id(),
902 QString::number( static_cast< int >( aggregate ) ),
903 subExpression,
904 parameters.filter,
905 orderBy );
906 if ( context->hasCachedValue( cacheKey ) )
907 return context->cachedValue( cacheKey );
908
909 QVariant result;
910 ok = false;
911
912
913 QgsExpressionContext subContext( *context );
914 QString error;
915 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
916
917 if ( !ok )
918 {
919 if ( !error.isEmpty() )
920 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
921 else
922 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
923 return QVariant();
924 }
925
926 // cache value
927 context->setCachedValue( cacheKey, result );
928 return result;
929}
930
931
932static QVariant fcnAggregateGeneric( Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
933{
934 if ( !context )
935 {
936 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
937 return QVariant();
938 }
939
940 // first step - find current layer
941
942 // TODO this expression function is NOT thread safe
944 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
946 if ( !vl )
947 {
948 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
949 return QVariant();
950 }
951
952 //lazy eval, so we need to evaluate nodes now
953
954 //first node is subexpression (or field name)
955 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
957 QString subExpression = node->dump();
958
959 //optional second node is group by
960 QString groupBy;
961 if ( values.count() > 1 )
962 {
963 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
965 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
966 if ( !nl || nl->value().isValid() )
967 groupBy = node->dump();
968 }
969
970 //optional third node is filter
971 if ( values.count() > 2 )
972 {
973 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
975 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
976 if ( !nl || nl->value().isValid() )
977 parameters.filter = node->dump();
978 }
979
980 //optional order by node, if supported
981 QString orderBy;
982 if ( orderByPos >= 0 && values.count() > orderByPos )
983 {
984 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
986 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
987 if ( !nl || nl->value().isValid() )
988 {
989 orderBy = node->dump();
990 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
991 }
992 }
993
994 // build up filter with group by
995
996 // find current group by value
997 if ( !groupBy.isEmpty() )
998 {
999 QgsExpression groupByExp( groupBy );
1000 QVariant groupByValue = groupByExp.evaluate( context );
1001 QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
1002 QgsVariantUtils::isNull( groupByValue ) ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
1003 QgsExpression::quotedValue( groupByValue ) );
1004 if ( !parameters.filter.isEmpty() )
1005 parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
1006 else
1007 parameters.filter = groupByClause;
1008 }
1009
1010 QgsExpression subExp( subExpression );
1011 QgsExpression filterExp( parameters.filter );
1012
1013 bool isStatic = true;
1014 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1015 for ( const QString &varName : refVars )
1016 {
1017 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1018 if ( scope && !scope->isStatic( varName ) )
1019 {
1020 isStatic = false;
1021 break;
1022 }
1023 }
1024
1025 QString cacheKey;
1026 if ( !isStatic )
1027 {
1028 bool ok = false;
1029 const QString contextHash = context->uniqueHash( ok, refVars );
1030 if ( ok )
1031 {
1032 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
1033 orderBy, contextHash );
1034 }
1035 }
1036 else
1037 {
1038 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1039 }
1040
1041 if ( context->hasCachedValue( cacheKey ) )
1042 return context->cachedValue( cacheKey );
1043
1044 QVariant result;
1045 bool ok = false;
1046
1047 QgsExpressionContext subContext( *context );
1049 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
1050 subContext.appendScope( subScope );
1051 QString error;
1052 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1053
1054 if ( !ok )
1055 {
1056 if ( !error.isEmpty() )
1057 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1058 else
1059 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1060 return QVariant();
1061 }
1062
1063 // cache value
1064 context->setCachedValue( cacheKey, result );
1065 return result;
1066}
1067
1068
1069static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1070{
1071 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1072}
1073
1074static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1075{
1076 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1077}
1078
1079static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1080{
1081 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1082}
1083
1084static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1085{
1086 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1087}
1088
1089static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1090{
1091 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1092}
1093
1094static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1095{
1096 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1097}
1098
1099static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1100{
1101 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1102}
1103
1104static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1105{
1106 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1107}
1108
1109static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1110{
1111 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1112}
1113
1114static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1115{
1116 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1117}
1118
1119static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1120{
1121 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1122}
1123
1124static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1125{
1126 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1127}
1128
1129static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1130{
1131 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1132}
1133
1134static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1135{
1136 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1137}
1138
1139static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1140{
1141 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1142}
1143
1144static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1145{
1146 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1147}
1148
1149static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1150{
1151 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1152}
1153
1154static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1155{
1156 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1157}
1158
1159static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1160{
1162
1163 //fourth node is concatenator
1164 if ( values.count() > 3 )
1165 {
1166 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1168 QVariant value = node->eval( parent, context );
1170 parameters.delimiter = value.toString();
1171 }
1172
1173 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1174}
1175
1176static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1177{
1179
1180 //fourth node is concatenator
1181 if ( values.count() > 3 )
1182 {
1183 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1185 QVariant value = node->eval( parent, context );
1187 parameters.delimiter = value.toString();
1188 }
1189
1190 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1191}
1192
1193static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1194{
1195 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1196}
1197
1198static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1199{
1200 if ( !context )
1201 return QVariant();
1202
1203 QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1204 bool ok = false;
1205 if ( QgsVariantUtils::isNull( scale ) )
1206 return QVariant();
1207
1208 const double v = scale.toDouble( &ok );
1209 if ( ok )
1210 return v;
1211 return QVariant();
1212}
1213
1214static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1215{
1216 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1217 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1218 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1219
1220 // force testValue to sit inside the range specified by the min and max value
1221 if ( testValue <= minValue )
1222 {
1223 return QVariant( minValue );
1224 }
1225 else if ( testValue >= maxValue )
1226 {
1227 return QVariant( maxValue );
1228 }
1229 else
1230 {
1231 return QVariant( testValue );
1232 }
1233}
1234
1235static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1236{
1237 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1238 return QVariant( std::floor( x ) );
1239}
1240
1241static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1242{
1243 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1244 return QVariant( std::ceil( x ) );
1245}
1246
1247static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1248{
1249 const QVariant value = values.at( 0 );
1250 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1251 {
1252 return QVariant( false );
1253 }
1254 else if ( value.userType() == QMetaType::QString )
1255 {
1256 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1257 return QVariant( !value.toString().isEmpty() );
1258 }
1259 else if ( QgsExpressionUtils::isList( value ) )
1260 {
1261 return !value.toList().isEmpty();
1262 }
1263 return QVariant( value.toBool() );
1264}
1265static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1266{
1267 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1268}
1269static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1270{
1271 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1272}
1273static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1274{
1275 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1276}
1277
1278static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1279{
1280 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1281 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1282 if ( format.isEmpty() && !language.isEmpty() )
1283 {
1284 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1285 return QVariant( QDateTime() );
1286 }
1287
1288 if ( format.isEmpty() && language.isEmpty() )
1289 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1290
1291 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1292 QLocale locale = QLocale();
1293 if ( !language.isEmpty() )
1294 {
1295 locale = QLocale( language );
1296 }
1297
1298 QDateTime datetime = locale.toDateTime( datetimestring, format );
1299 if ( !datetime.isValid() )
1300 {
1301 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1302 datetime = QDateTime();
1303 }
1304 return QVariant( datetime );
1305}
1306
1307static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1308{
1309 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1310 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1311 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1312
1313 const QDate date( year, month, day );
1314 if ( !date.isValid() )
1315 {
1316 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1317 return QVariant();
1318 }
1319 return QVariant( date );
1320}
1321
1322static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1323{
1324 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1325 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1326 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1327
1328 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1329 if ( !time.isValid() )
1330 {
1331 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1332 return QVariant();
1333 }
1334 return QVariant( time );
1335}
1336
1337static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1338{
1339 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1340 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1341 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1342 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1343 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1344 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1345
1346 const QDate date( year, month, day );
1347 if ( !date.isValid() )
1348 {
1349 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1350 return QVariant();
1351 }
1352 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1353 if ( !time.isValid() )
1354 {
1355 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1356 return QVariant();
1357 }
1358 return QVariant( QDateTime( date, time ) );
1359}
1360
1361static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1362{
1363 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1364 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1365 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1366 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1367 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1368 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1369 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1370
1371 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1372}
1373
1374static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1375{
1376 for ( const QVariant &value : values )
1377 {
1378 if ( QgsVariantUtils::isNull( value ) )
1379 continue;
1380 return value;
1381 }
1382 return QVariant();
1383}
1384
1385static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1386{
1387 const QVariant val1 = values.at( 0 );
1388 const QVariant val2 = values.at( 1 );
1389
1390 if ( val1 == val2 )
1391 return QVariant();
1392 else
1393 return val1;
1394}
1395
1396static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1397{
1398 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1399 return QVariant( str.toLower() );
1400}
1401static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1402{
1403 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1404 return QVariant( str.toUpper() );
1405}
1406static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1407{
1408 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1409 QStringList elems = str.split( ' ' );
1410 for ( int i = 0; i < elems.size(); i++ )
1411 {
1412 if ( elems[i].size() > 1 )
1413 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1414 }
1415 return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1416}
1417
1418static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1419{
1420 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1421 return QVariant( str.trimmed() );
1422}
1423
1424static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1425{
1426 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1427
1428 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1429
1430 const QRegularExpression re( QStringLiteral( "^([%1]*)" ).arg( QRegularExpression::escape( characters ) ) );
1431 str.replace( re, QString() );
1432 return QVariant( str );
1433}
1434
1435static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1436{
1437 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1438
1439 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1440
1441 const QRegularExpression re( QStringLiteral( "([%1]*)$" ).arg( QRegularExpression::escape( characters ) ) );
1442 str.replace( re, QString() );
1443 return QVariant( str );
1444}
1445
1446static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1447{
1448 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1449 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1450 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1451}
1452
1453static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1454{
1455 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1456 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1457 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1458}
1459
1460static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1461{
1462 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1463 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1464 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1465 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1466}
1467
1468static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1469{
1470 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1471 return QVariant( QgsStringUtils::soundex( string ) );
1472}
1473
1474static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1475{
1476 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1477 return QVariant( QString( character ) );
1478}
1479
1480static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1481{
1482 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1483
1484 if ( value.isEmpty() )
1485 {
1486 return QVariant();
1487 }
1488
1489 int res = value.at( 0 ).unicode();
1490 return QVariant( res );
1491}
1492
1493static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1494{
1495 if ( values.length() == 2 || values.length() == 3 )
1496 {
1497 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1498 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1499
1500 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1501
1502 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1503 }
1504
1505 return QVariant();
1506}
1507
1508static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1509{
1510 // two variants, one for geometry, one for string
1511
1512 //geometry variant
1513 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1514 if ( !geom.isNull() )
1515 {
1516 if ( geom.type() == Qgis::GeometryType::Line )
1517 return QVariant( geom.length() );
1518 else
1519 return QVariant();
1520 }
1521
1522 //otherwise fall back to string variant
1523 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1524 return QVariant( str.length() );
1525}
1526
1527static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1528{
1529 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1530
1531 if ( geom.type() != Qgis::GeometryType::Line )
1532 return QVariant();
1533
1534 double totalLength = 0;
1535 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1536 {
1537 if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *it ) )
1538 {
1539 totalLength += line->length3D();
1540 }
1541 else
1542 {
1543 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1544 totalLength += segmentized->length3D();
1545 }
1546 }
1547
1548 return totalLength;
1549}
1550
1551static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1552{
1553 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1554 {
1555 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1556 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1557 QVector< QPair< QString, QString > > mapItems;
1558
1559 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1560 {
1561 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1562 }
1563
1564 // larger keys should be replaced first since they may contain whole smaller keys
1565 std::sort( mapItems.begin(),
1566 mapItems.end(),
1567 []( const QPair< QString, QString > &pair1,
1568 const QPair< QString, QString > &pair2 )
1569 {
1570 return ( pair1.first.length() > pair2.first.length() );
1571 } );
1572
1573 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1574 {
1575 str = str.replace( it->first, it->second );
1576 }
1577
1578 return QVariant( str );
1579 }
1580 else if ( values.count() == 3 )
1581 {
1582 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1583 QVariantList before;
1584 QVariantList after;
1585 bool isSingleReplacement = false;
1586
1587 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1588 {
1589 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1590 }
1591 else
1592 {
1593 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1594 }
1595
1596 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1597 {
1598 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1599 isSingleReplacement = true;
1600 }
1601 else
1602 {
1603 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1604 }
1605
1606 if ( !isSingleReplacement && before.length() != after.length() )
1607 {
1608 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1609 return QVariant();
1610 }
1611
1612 for ( int i = 0; i < before.length(); i++ )
1613 {
1614 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1615 }
1616
1617 return QVariant( str );
1618 }
1619 else
1620 {
1621 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1622 return QVariant();
1623 }
1624}
1625
1626static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1627{
1628 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1629 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1630 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1631
1632 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1633 if ( !re.isValid() )
1634 {
1635 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1636 return QVariant();
1637 }
1638 return QVariant( str.replace( re, after ) );
1639}
1640
1641static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1642{
1643 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1644 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1645
1646 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1647 if ( !re.isValid() )
1648 {
1649 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1650 return QVariant();
1651 }
1652 return QVariant( ( str.indexOf( re ) + 1 ) );
1653}
1654
1655static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1656{
1657 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1658 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1659 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1660
1661 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1662 if ( !re.isValid() )
1663 {
1664 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1665 return QVariant();
1666 }
1667
1668 QRegularExpressionMatch matches = re.match( str );
1669 if ( matches.hasMatch() )
1670 {
1671 QVariantList array;
1672 QStringList list = matches.capturedTexts();
1673
1674 // Skip the first string to only return captured groups
1675 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1676 {
1677 array += ( !( *it ).isEmpty() ) ? *it : empty;
1678 }
1679
1680 return QVariant( array );
1681 }
1682 else
1683 {
1684 return QVariant();
1685 }
1686}
1687
1688static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1689{
1690 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1691 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1692
1693 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1694 if ( !re.isValid() )
1695 {
1696 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1697 return QVariant();
1698 }
1699
1700 // extract substring
1701 QRegularExpressionMatch match = re.match( str );
1702 if ( match.hasMatch() )
1703 {
1704 // return first capture
1705 if ( match.lastCapturedIndex() > 0 )
1706 {
1707 // a capture group was present, so use that
1708 return QVariant( match.captured( 1 ) );
1709 }
1710 else
1711 {
1712 // no capture group, so using all match
1713 return QVariant( match.captured( 0 ) );
1714 }
1715 }
1716 else
1717 {
1718 return QVariant( "" );
1719 }
1720}
1721
1722static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1723{
1724 QString uuid = QUuid::createUuid().toString();
1725 if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1726 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1727 else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1728 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1729 return uuid;
1730}
1731
1732static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1733{
1734 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1735 return QVariant();
1736
1737 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1738 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1739
1740 int len = 0;
1741 if ( values.at( 2 ).isValid() )
1742 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1743 else
1744 len = str.size();
1745
1746 if ( from < 0 )
1747 {
1748 from = str.size() + from;
1749 if ( from < 0 )
1750 {
1751 from = 0;
1752 }
1753 }
1754 else if ( from > 0 )
1755 {
1756 //account for the fact that substr() starts at 1
1757 from -= 1;
1758 }
1759
1760 if ( len < 0 )
1761 {
1762 len = str.size() + len - from;
1763 if ( len < 0 )
1764 {
1765 len = 0;
1766 }
1767 }
1768
1769 return QVariant( str.mid( from, len ) );
1770}
1771static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1772{
1773 FEAT_FROM_CONTEXT( context, f )
1774 return QVariant( f.id() );
1775}
1776
1777static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1778{
1779 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1780 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1781 bool foundLayer = false;
1782 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, geom]( QgsMapLayer * mapLayer )
1783 {
1784 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1785 if ( !layer || !layer->dataProvider() )
1786 {
1787 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1788 return QVariant();
1789 }
1790
1791 if ( bandNb < 1 || bandNb > layer->bandCount() )
1792 {
1793 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1794 return QVariant();
1795 }
1796
1797 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1798 {
1799 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1800 return QVariant();
1801 }
1802
1803 QgsPointXY point = geom.asPoint();
1804 if ( geom.isMultipart() )
1805 {
1806 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1807 if ( multiPoint.count() == 1 )
1808 {
1809 point = multiPoint[0];
1810 }
1811 else
1812 {
1813 // if the geometry contains more than one part, return an undefined value
1814 return QVariant();
1815 }
1816 }
1817
1818 double value = layer->dataProvider()->sample( point, bandNb );
1819 return std::isnan( value ) ? QVariant() : value;
1820 },
1821 foundLayer );
1822
1823 if ( !foundLayer )
1824 {
1825 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1826 return QVariant();
1827 }
1828 else
1829 {
1830 return res;
1831 }
1832}
1833
1834static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1835{
1836 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1837 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1838
1839 bool foundLayer = false;
1840 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, value]( QgsMapLayer * mapLayer )-> QVariant
1841 {
1842 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1843 if ( !layer || !layer->dataProvider() )
1844 {
1845 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1846 return QVariant();
1847 }
1848
1849 if ( bandNb < 1 || bandNb > layer->bandCount() )
1850 {
1851 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1852 return QVariant();
1853 }
1854
1855 if ( std::isnan( value ) )
1856 {
1857 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1858 return QVariant();
1859 }
1860
1861 if ( ! layer->dataProvider()->attributeTable( bandNb ) )
1862 {
1863 return QVariant();
1864 }
1865
1866 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1867 if ( data.isEmpty() )
1868 {
1869 return QVariant();
1870 }
1871
1872 QVariantMap result;
1873 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1874 for ( int idx = 0; idx < static_cast<int>( fields.count( ) ) && idx < static_cast<int>( data.count() ); ++idx )
1875 {
1876 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1877 if ( field.isColor() || field.isRamp() )
1878 {
1879 continue;
1880 }
1881 result.insert( fields.at( idx ).name, data.at( idx ) );
1882 }
1883
1884 return result;
1885 }, foundLayer );
1886
1887 if ( !foundLayer )
1888 {
1889 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1890 return QVariant();
1891 }
1892 else
1893 {
1894 return res;
1895 }
1896}
1897
1898static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1899{
1900 if ( !context )
1901 return QVariant();
1902
1903 return context->feature();
1904}
1905
1906static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1907{
1908 QgsFeature feature;
1909 QString attr;
1910 if ( values.size() == 1 )
1911 {
1912 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1913 feature = context->feature();
1914 }
1915 else if ( values.size() == 2 )
1916 {
1917 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1918 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1919 }
1920 else
1921 {
1922 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
1923 return QVariant();
1924 }
1925
1926 return feature.attribute( attr );
1927}
1928
1929static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1930{
1931 QString table { R"html(
1932 <table>
1933 <thead>
1934 <tr><th>%1</th></tr>
1935 </thead>
1936 <tbody>
1937 <tr><td>%2</td></tr>
1938 </tbody>
1939 </table>)html" };
1940 QVariantMap dict;
1941 if ( values.size() == 1 )
1942 {
1943 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1944 }
1945 else
1946 {
1947 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
1948 return QVariant();
1949 }
1950
1951 if ( dict.isEmpty() )
1952 {
1953 return QVariant();
1954 }
1955
1956 QStringList headers;
1957 QStringList cells;
1958
1959 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1960 {
1961 headers.push_back( it.key().toHtmlEscaped() );
1962 cells.push_back( it.value().toString( ).toHtmlEscaped() );
1963 }
1964
1965 return table.arg( headers.join( QLatin1String( "</th><th>" ) ), cells.join( QLatin1String( "</td><td>" ) ) );
1966}
1967
1968static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1969{
1970 QString table { R"html(
1971 <dl>
1972 %1
1973 </dl>)html" };
1974 QVariantMap dict;
1975 if ( values.size() == 1 )
1976 {
1977 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1978 }
1979 else
1980 {
1981 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
1982 return QVariant();
1983 }
1984
1985 if ( dict.isEmpty() )
1986 {
1987 return QVariant();
1988 }
1989
1990 QString rows;
1991
1992 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1993 {
1994 rows.append( QStringLiteral( "<dt>%1</dt><dd>%2</dd>" ).arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
1995 }
1996
1997 return table.arg( rows );
1998}
1999
2000static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2001{
2002 QVariant layer;
2003 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2004 {
2005 layer = context->variable( QStringLiteral( "layer" ) );
2006 }
2007 else
2008 {
2009 //first node is layer id or name
2010 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2012 layer = node->eval( parent, context );
2014 }
2015
2016 QgsFeature feature;
2017 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2018 {
2019 feature = context->feature();
2020 }
2021 else
2022 {
2023 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2024 }
2025
2027 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2028 if ( strength == QLatin1String( "hard" ) )
2029 {
2031 }
2032 else if ( strength == QLatin1String( "soft" ) )
2033 {
2035 }
2036
2037 bool foundLayer = false;
2038 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2039 {
2040 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2041 if ( !layer )
2042 {
2043 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2044 return QVariant();
2045 }
2046
2047 const QgsFields fields = layer->fields();
2048 bool valid = true;
2049 for ( int i = 0; i < fields.size(); i++ )
2050 {
2051 QStringList errors;
2052 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2053 if ( !valid )
2054 {
2055 break;
2056 }
2057 }
2058
2059 return valid;
2060 }, foundLayer );
2061
2062 if ( !foundLayer )
2063 {
2064 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2065 return QVariant();
2066 }
2067
2068 return res;
2069}
2070
2071static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2072{
2073 QVariant layer;
2074 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2075 {
2076 layer = context->variable( QStringLiteral( "layer" ) );
2077 }
2078 else
2079 {
2080 //first node is layer id or name
2081 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2083 layer = node->eval( parent, context );
2085 }
2086
2087 QgsFeature feature;
2088 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2089 {
2090 feature = context->feature();
2091 }
2092 else
2093 {
2094 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2095 }
2096
2098 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2099 if ( strength == QLatin1String( "hard" ) )
2100 {
2102 }
2103 else if ( strength == QLatin1String( "soft" ) )
2104 {
2106 }
2107
2108 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2109
2110 bool foundLayer = false;
2111 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, attributeName, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2112 {
2113 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2114 if ( !layer )
2115 {
2116 return QVariant();
2117 }
2118
2119 const int fieldIndex = layer->fields().indexFromName( attributeName );
2120 if ( fieldIndex == -1 )
2121 {
2122 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2123 return QVariant();
2124 }
2125
2126 QStringList errors;
2127 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2128 return valid;
2129 }, foundLayer );
2130
2131 if ( !foundLayer )
2132 {
2133 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2134 return QVariant();
2135 }
2136
2137 return res;
2138}
2139
2140static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2141{
2142 QgsFeature feature;
2143 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2144 {
2145 feature = context->feature();
2146 }
2147 else
2148 {
2149 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2150 }
2151
2152 const QgsFields fields = feature.fields();
2153 QVariantMap result;
2154 for ( int i = 0; i < fields.count(); ++i )
2155 {
2156 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2157 }
2158 return result;
2159}
2160
2161static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2162{
2163 QgsVectorLayer *layer = nullptr;
2164 QgsFeature feature;
2165
2166 // TODO this expression function is NOT thread safe
2168 if ( values.isEmpty() )
2169 {
2170 feature = context->feature();
2171 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2172 }
2173 else if ( values.size() == 1 )
2174 {
2175 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2176 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2177 }
2178 else if ( values.size() == 2 )
2179 {
2180 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2181 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2182 }
2183 else
2184 {
2185 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2186 return QVariant();
2187 }
2189
2190 if ( !layer )
2191 {
2192 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2193 return QVariant();
2194 }
2195
2196 if ( !feature.isValid() )
2197 {
2198 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2199 return QVariant();
2200 }
2201
2202 const QgsFields fields = feature.fields();
2203 QVariantMap result;
2204 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2205 {
2206 const QString fieldName { fields.at( fieldIndex ).name() };
2207 const QVariant attributeVal = feature.attribute( fieldIndex );
2208 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
2209 if ( context && context->hasCachedValue( cacheValueKey ) )
2210 {
2211 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2212 }
2213 else
2214 {
2215 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2217 QVariant cache;
2218 if ( context )
2219 {
2220 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
2221
2222 if ( !context->hasCachedValue( cacheKey ) )
2223 {
2224 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2225 context->setCachedValue( cacheKey, cache );
2226 }
2227 else
2228 {
2229 cache = context->cachedValue( cacheKey );
2230 }
2231 }
2232 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2233
2234 result.insert( fields.at( fieldIndex ).name(), value );
2235
2236 if ( context )
2237 {
2238 context->setCachedValue( cacheValueKey, value );
2239 }
2240
2241 }
2242 }
2243 return result;
2244}
2245
2246static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2247{
2248 QgsVectorLayer *layer = nullptr;
2249 QgsFeature feature;
2250 bool evaluate = true;
2251
2252 // TODO this expression function is NOT thread safe
2254 if ( values.isEmpty() )
2255 {
2256 feature = context->feature();
2257 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2258 }
2259 else if ( values.size() == 1 )
2260 {
2261 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2262 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2263 }
2264 else if ( values.size() == 2 )
2265 {
2266 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2267 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2268 }
2269 else if ( values.size() == 3 )
2270 {
2271 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2272 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2273 evaluate = values.value( 2 ).toBool();
2274 }
2275 else
2276 {
2277 if ( isMaptip )
2278 {
2279 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2280 }
2281 else
2282 {
2283 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2284 }
2285 return QVariant();
2286 }
2287
2288 if ( !layer )
2289 {
2290 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2291 return QVariant( );
2292 }
2294
2295 if ( !feature.isValid() )
2296 {
2297 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2298 return QVariant( );
2299 }
2300
2301 if ( ! evaluate )
2302 {
2303 if ( isMaptip )
2304 {
2305 return layer->mapTipTemplate();
2306 }
2307 else
2308 {
2309 return layer->displayExpression();
2310 }
2311 }
2312
2313 QgsExpressionContext subContext( *context );
2314 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2315 subContext.setFeature( feature );
2316
2317 if ( isMaptip )
2318 {
2319 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2320 }
2321 else
2322 {
2323 QgsExpression exp( layer->displayExpression() );
2324 exp.prepare( &subContext );
2325 return exp.evaluate( &subContext ).toString();
2326 }
2327}
2328
2329static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2330{
2331 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2332}
2333
2334static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2335{
2336 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2337}
2338
2339static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2340{
2341 QgsFeature feature;
2342 QVariant layer;
2343 if ( values.isEmpty() )
2344 {
2345 feature = context->feature();
2346 layer = context->variable( QStringLiteral( "layer" ) );
2347 }
2348 else if ( values.size() == 1 )
2349 {
2350 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2351 layer = context->variable( QStringLiteral( "layer" ) );
2352 }
2353 else if ( values.size() == 2 )
2354 {
2355 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2356 layer = values.at( 0 );
2357 }
2358 else
2359 {
2360 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2361 return QVariant();
2362 }
2363
2364 bool foundLayer = false;
2365 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [feature]( QgsMapLayer * mapLayer ) -> QVariant
2366 {
2367 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2368 if ( !layer || !feature.isValid() )
2369 {
2370 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2371 }
2372
2373 return layer->selectedFeatureIds().contains( feature.id() );
2374 }, foundLayer );
2375 if ( !foundLayer )
2376 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2377 else
2378 return res;
2379}
2380
2381static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2382{
2383 QVariant layer;
2384
2385 if ( values.isEmpty() )
2386 layer = context->variable( QStringLiteral( "layer" ) );
2387 else if ( values.count() == 1 )
2388 layer = values.at( 0 );
2389 else
2390 {
2391 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2392 return QVariant();
2393 }
2394
2395 bool foundLayer = false;
2396 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, []( QgsMapLayer * mapLayer ) -> QVariant
2397 {
2398 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2399 if ( !layer )
2400 {
2401 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2402 }
2403
2404 return layer->selectedFeatureCount();
2405 }, foundLayer );
2406 if ( !foundLayer )
2407 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2408 else
2409 return res;
2410}
2411
2412static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2413{
2414 static QMap<QString, qlonglong> counterCache;
2415 QVariant functionResult;
2416
2417 auto fetchAndIncrementFunc = [ values, parent, &functionResult ]( QgsMapLayer * mapLayer, const QString & databaseArgument )
2418 {
2419 QString database;
2420
2421 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2422
2423 if ( layer )
2424 {
2425 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2426 database = decodedUri.value( QStringLiteral( "path" ) ).toString();
2427 if ( database.isEmpty() )
2428 {
2429 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2430 }
2431 }
2432 else
2433 {
2434 database = databaseArgument;
2435 }
2436
2437 const QString table = values.at( 1 ).toString();
2438 const QString idColumn = values.at( 2 ).toString();
2439 const QString filterAttribute = values.at( 3 ).toString();
2440 const QVariant filterValue = values.at( 4 ).toString();
2441 const QVariantMap defaultValues = values.at( 5 ).toMap();
2442
2443 // read from database
2445 sqlite3_statement_unique_ptr sqliteStatement;
2446
2447 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2448 {
2449 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2450 functionResult = QVariant();
2451 return;
2452 }
2453
2454 QString errorMessage;
2455 QString currentValSql;
2456
2457 qlonglong nextId = 0;
2458 bool cachedMode = false;
2459 bool valueRetrieved = false;
2460
2461 QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2462
2463 // Running in transaction mode, check for cached value first
2464 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2465 {
2466 cachedMode = true;
2467
2468 auto cachedCounter = counterCache.find( cacheString );
2469
2470 if ( cachedCounter != counterCache.end() )
2471 {
2472 qlonglong &cachedValue = cachedCounter.value();
2473 nextId = cachedValue;
2474 nextId += 1;
2475 cachedValue = nextId;
2476 valueRetrieved = true;
2477 }
2478 }
2479
2480 // Either not in cached mode or no cached value found, obtain from DB
2481 if ( !cachedMode || !valueRetrieved )
2482 {
2483 int result = SQLITE_ERROR;
2484
2485 currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2486 if ( !filterAttribute.isNull() )
2487 {
2488 currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2489 }
2490
2491 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2492
2493 if ( result == SQLITE_OK )
2494 {
2495 nextId = 0;
2496 if ( sqliteStatement.step() == SQLITE_ROW )
2497 {
2498 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2499 }
2500
2501 // If in cached mode: add value to cache and connect to transaction
2502 if ( cachedMode && result == SQLITE_OK )
2503 {
2504 counterCache.insert( cacheString, nextId );
2505
2506 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2507 {
2508 counterCache.remove( cacheString );
2509 } );
2510 }
2511 valueRetrieved = true;
2512 }
2513 }
2514
2515 if ( valueRetrieved )
2516 {
2517 QString upsertSql;
2518 upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
2519 QStringList cols;
2520 QStringList vals;
2521 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2522 vals << QgsSqliteUtils::quotedValue( nextId );
2523
2524 if ( !filterAttribute.isNull() )
2525 {
2526 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2527 vals << QgsSqliteUtils::quotedValue( filterValue );
2528 }
2529
2530 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2531 {
2532 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2533 vals << iter.value().toString();
2534 }
2535
2536 upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
2537 upsertSql += QLatin1String( " VALUES " );
2538 upsertSql += '(' + vals.join( ',' ) + ')';
2539
2540 int result = SQLITE_ERROR;
2541 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2542 {
2543 QgsTransaction *transaction = layer->dataProvider()->transaction();
2544 if ( transaction->executeSql( upsertSql, errorMessage ) )
2545 {
2546 result = SQLITE_OK;
2547 }
2548 }
2549 else
2550 {
2551 result = sqliteDb.exec( upsertSql, errorMessage );
2552 }
2553 if ( result == SQLITE_OK )
2554 {
2555 functionResult = QVariant( nextId );
2556 return;
2557 }
2558 else
2559 {
2560 parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2561 functionResult = QVariant();
2562 return;
2563 }
2564 }
2565
2566 functionResult = QVariant();
2567 };
2568
2569 bool foundLayer = false;
2570 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer * layer )
2571 {
2572 fetchAndIncrementFunc( layer, QString() );
2573 }, foundLayer );
2574 if ( !foundLayer )
2575 {
2576 const QString databasePath = values.at( 0 ).toString();
2577 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath]
2578 {
2579 fetchAndIncrementFunc( nullptr, databasePath );
2580 } );
2581 }
2582
2583 return functionResult;
2584}
2585
2586static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2587{
2588 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2589 if ( !crs.isValid() )
2590 return QVariant();
2591 return crs.authid();
2592}
2593
2594static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2595{
2596 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2597 QgsCoordinateReferenceSystem crs( definition );
2598
2599 if ( !crs.isValid() )
2600 {
2601 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to cordinate reference system" ).arg( definition ) );
2602 }
2603
2604 return QVariant::fromValue( crs );
2605}
2606
2607static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2608{
2609 QString concat;
2610 for ( const QVariant &value : values )
2611 {
2612 if ( !QgsVariantUtils::isNull( value ) )
2613 concat += QgsExpressionUtils::getStringValue( value, parent );
2614 }
2615 return concat;
2616}
2617
2618static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2619{
2620 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2621 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2622}
2623
2624static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2625{
2626 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2627 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2628 return string.right( pos );
2629}
2630
2631static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2632{
2633 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2634 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2635 return string.left( pos );
2636}
2637
2638static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2639{
2640 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2641 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2642 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2643 return string.leftJustified( length, fill.at( 0 ), true );
2644}
2645
2646static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2647{
2648 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2649 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2650 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2651 return string.rightJustified( length, fill.at( 0 ), true );
2652}
2653
2654static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2655{
2656 if ( values.size() < 1 )
2657 {
2658 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2659 return QVariant();
2660 }
2661
2662 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2663 for ( int n = 1; n < values.length(); n++ )
2664 {
2665 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2666 }
2667 return string;
2668}
2669
2670
2671static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2672{
2673 return QVariant( QDateTime::currentDateTime() );
2674}
2675
2676static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2677{
2678 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2679 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2680 if ( format.isEmpty() && !language.isEmpty() )
2681 {
2682 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2683 return QVariant( QDate() );
2684 }
2685
2686 if ( format.isEmpty() && language.isEmpty() )
2687 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2688
2689 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2690 QLocale locale = QLocale();
2691 if ( !language.isEmpty() )
2692 {
2693 locale = QLocale( language );
2694 }
2695
2696 QDate date = locale.toDate( datestring, format );
2697 if ( !date.isValid() )
2698 {
2699 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2700 date = QDate();
2701 }
2702 return QVariant( date );
2703}
2704
2705static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2706{
2707 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2708 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2709 if ( format.isEmpty() && !language.isEmpty() )
2710 {
2711 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2712 return QVariant( QTime() );
2713 }
2714
2715 if ( format.isEmpty() && language.isEmpty() )
2716 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2717
2718 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2719 QLocale locale = QLocale();
2720 if ( !language.isEmpty() )
2721 {
2722 locale = QLocale( language );
2723 }
2724
2725 QTime time = locale.toTime( timestring, format );
2726 if ( !time.isValid() )
2727 {
2728 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2729 time = QTime();
2730 }
2731 return QVariant( time );
2732}
2733
2734static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2735{
2736 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2737}
2738
2739/*
2740 * DMS functions
2741 */
2742
2743static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2744{
2745 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2746 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2747 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2748
2749 QString formatString;
2750 if ( values.count() > 3 )
2751 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2752
2754 if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2755 {
2757 }
2758 else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2759 {
2761 }
2762 else if ( ! formatString.isEmpty() )
2763 {
2764 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2765 return QVariant();
2766 }
2767
2768 if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2769 {
2770 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2771 }
2772 else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2773 {
2774 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2775 }
2776 else
2777 {
2778 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2779 return QVariant();
2780 }
2781}
2782
2783static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2784{
2786 return floatToDegreeFormat( format, values, context, parent, node );
2787}
2788
2789static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2790{
2791 double value = 0.0;
2792 bool ok = false;
2793 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2794
2795 return ok ? QVariant( value ) : QVariant();
2796}
2797
2798static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2799{
2801 return floatToDegreeFormat( format, values, context, parent, node );
2802}
2803
2804static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2805{
2806 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2807 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2808 qint64 seconds = d2.secsTo( d1 );
2809 return QVariant::fromValue( QgsInterval( seconds ) );
2810}
2811
2812static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2813{
2814 if ( !values.at( 0 ).canConvert<QDate>() )
2815 return QVariant();
2816
2817 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2818 if ( !date.isValid() )
2819 return QVariant();
2820
2821 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2822 // (to match PostgreSQL behavior)
2823 return date.dayOfWeek() % 7;
2824}
2825
2826static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2827{
2828 QVariant value = values.at( 0 );
2829 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2830 if ( inter.isValid() )
2831 {
2832 return QVariant( inter.days() );
2833 }
2834 else
2835 {
2836 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2837 return QVariant( d1.date().day() );
2838 }
2839}
2840
2841static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2842{
2843 QVariant value = values.at( 0 );
2844 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2845 if ( inter.isValid() )
2846 {
2847 return QVariant( inter.years() );
2848 }
2849 else
2850 {
2851 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2852 return QVariant( d1.date().year() );
2853 }
2854}
2855
2856static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2857{
2858 QVariant value = values.at( 0 );
2859 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2860 if ( inter.isValid() )
2861 {
2862 return QVariant( inter.months() );
2863 }
2864 else
2865 {
2866 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2867 return QVariant( d1.date().month() );
2868 }
2869}
2870
2871static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2872{
2873 QVariant value = values.at( 0 );
2874 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2875 if ( inter.isValid() )
2876 {
2877 return QVariant( inter.weeks() );
2878 }
2879 else
2880 {
2881 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2882 return QVariant( d1.date().weekNumber() );
2883 }
2884}
2885
2886static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2887{
2888 QVariant value = values.at( 0 );
2889 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2890 if ( inter.isValid() )
2891 {
2892 return QVariant( inter.hours() );
2893 }
2894 else
2895 {
2896 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2897 return QVariant( t1.hour() );
2898 }
2899}
2900
2901static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2902{
2903 QVariant value = values.at( 0 );
2904 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2905 if ( inter.isValid() )
2906 {
2907 return QVariant( inter.minutes() );
2908 }
2909 else
2910 {
2911 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2912 return QVariant( t1.minute() );
2913 }
2914}
2915
2916static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2917{
2918 QVariant value = values.at( 0 );
2919 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2920 if ( inter.isValid() )
2921 {
2922 return QVariant( inter.seconds() );
2923 }
2924 else
2925 {
2926 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2927 return QVariant( t1.second() );
2928 }
2929}
2930
2931static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2932{
2933 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2934 if ( dt.isValid() )
2935 {
2936 return QVariant( dt.toMSecsSinceEpoch() );
2937 }
2938 else
2939 {
2940 return QVariant();
2941 }
2942}
2943
2944static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2945{
2946 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2947 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2948 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2949}
2950
2951static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2952{
2953 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2954 if ( parent->hasEvalError() )
2955 {
2956 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif" ) ) );
2957 return QVariant();
2958 }
2959 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2960 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
2961}
2962
2963static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2965 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2966 if ( parent->hasEvalError() )
2967 {
2968 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif_geotag" ) ) );
2969 return QVariant();
2970 }
2971 bool ok;
2972 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
2973}
2974
2975#define ENSURE_GEOM_TYPE(f, g, geomtype) \
2976 if ( !(f).hasGeometry() ) \
2977 return QVariant(); \
2978 QgsGeometry g = (f).geometry(); \
2979 if ( (g).type() != (geomtype) ) \
2980 return QVariant();
2981
2982static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2983{
2984 FEAT_FROM_CONTEXT( context, f )
2986 if ( g.isMultipart() )
2987 {
2988 return g.asMultiPoint().at( 0 ).x();
2989 }
2990 else
2991 {
2992 return g.asPoint().x();
2993 }
2994}
2995
2996static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2997{
2998 FEAT_FROM_CONTEXT( context, f )
3000 if ( g.isMultipart() )
3001 {
3002 return g.asMultiPoint().at( 0 ).y();
3003 }
3004 else
3005 {
3006 return g.asPoint().y();
3007 }
3008}
3009
3010static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3011{
3012 FEAT_FROM_CONTEXT( context, f )
3014
3015 if ( g.isEmpty() )
3016 return QVariant();
3017
3018 const QgsAbstractGeometry *abGeom = g.constGet();
3019
3020 if ( g.isEmpty() || !abGeom->is3D() )
3021 return QVariant();
3022
3023 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3024 {
3025 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3026 if ( point )
3027 return point->z();
3028 }
3029 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3030 {
3031 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3032 {
3033 if ( collection->numGeometries() > 0 )
3034 {
3035 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3036 return point->z();
3037 }
3038 }
3039 }
3040
3041 return QVariant();
3042}
3043
3044static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3045{
3046 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3047 if ( geom.isNull() )
3048 return QVariant();
3049
3050 bool isValid = geom.isGeosValid();
3051
3052 return QVariant( isValid );
3053}
3054
3055static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3056{
3057 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3058 if ( geom.isNull() )
3059 return QVariant();
3060
3061 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3062#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
3064#else
3066#endif
3067 if ( methodString.compare( QLatin1String( "linework" ), Qt::CaseInsensitive ) == 0 )
3069 else if ( methodString.compare( QLatin1String( "structure" ), Qt::CaseInsensitive ) == 0 )
3071
3072 const bool keepCollapsed = values.value( 2 ).toBool();
3073
3074 QgsGeometry valid;
3075 try
3076 {
3077 valid = geom.makeValid( method, keepCollapsed );
3078 }
3079 catch ( QgsNotSupportedException & )
3080 {
3081 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3082 return QVariant();
3083 }
3084
3085 return QVariant::fromValue( valid );
3086}
3087
3088static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3089{
3090 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3091 if ( geom.isNull() )
3092 return QVariant();
3093
3094 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3095 QVariantList array;
3096 for ( int i = 0; i < multiGeom.size(); ++i )
3097 {
3098 array += QVariant::fromValue( multiGeom.at( i ) );
3099 }
3100
3101 return array;
3102}
3103
3104static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3105{
3106 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3107 if ( geom.isNull() )
3108 return QVariant();
3109
3110 //if single point, return the point's x coordinate
3111 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3112 {
3113 return geom.asPoint().x();
3114 }
3115
3116 //otherwise return centroid x
3117 QgsGeometry centroid = geom.centroid();
3118 QVariant result( centroid.asPoint().x() );
3119 return result;
3120}
3121
3122static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3123{
3124 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3125 if ( geom.isNull() )
3126 return QVariant();
3127
3128 //if single point, return the point's y coordinate
3129 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3130 {
3131 return geom.asPoint().y();
3132 }
3133
3134 //otherwise return centroid y
3135 QgsGeometry centroid = geom.centroid();
3136 QVariant result( centroid.asPoint().y() );
3137 return result;
3138}
3139
3140static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3141{
3142 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3143 if ( geom.isNull() )
3144 return QVariant(); //or 0?
3145
3146 if ( !geom.constGet()->is3D() )
3147 return QVariant();
3148
3149 //if single point, return the point's z coordinate
3150 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3151 {
3152 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3153 if ( point )
3154 return point->z();
3155 }
3156 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3157 {
3158 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3159 {
3160 if ( collection->numGeometries() == 1 )
3161 {
3162 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3163 return point->z();
3164 }
3165 }
3166 }
3167
3168 return QVariant();
3169}
3170
3171static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3172{
3173 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3174 if ( geom.isNull() )
3175 return QVariant(); //or 0?
3176
3177 if ( !geom.constGet()->isMeasure() )
3178 return QVariant();
3179
3180 //if single point, return the point's m value
3181 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3182 {
3183 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3184 if ( point )
3185 return point->m();
3186 }
3187 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3188 {
3189 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3190 {
3191 if ( collection->numGeometries() == 1 )
3192 {
3193 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3194 return point->m();
3195 }
3196 }
3197 }
3198
3199 return QVariant();
3200}
3201
3202static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3203{
3204 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3205
3206 if ( geom.isNull() )
3207 return QVariant();
3208
3209 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3210
3211 if ( idx < 0 )
3212 {
3213 //negative idx
3214 int count = geom.constGet()->nCoordinates();
3215 idx = count + idx;
3216 }
3217 else
3218 {
3219 //positive idx is 1 based
3220 idx -= 1;
3221 }
3222
3223 QgsVertexId vId;
3224 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3225 {
3226 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3227 return QVariant();
3228 }
3229
3230 QgsPoint point = geom.constGet()->vertexAt( vId );
3231 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3232}
3233
3234static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3235{
3236 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3237
3238 if ( geom.isNull() )
3239 return QVariant();
3240
3241 QgsVertexId vId;
3242 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3243 {
3244 return QVariant();
3245 }
3246
3247 QgsPoint point = geom.constGet()->vertexAt( vId );
3248 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3249}
3250
3251static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3252{
3253 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3254
3255 if ( geom.isNull() )
3256 return QVariant();
3257
3258 QgsVertexId vId;
3259 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3260 {
3261 return QVariant();
3262 }
3263
3264 QgsPoint point = geom.constGet()->vertexAt( vId );
3265 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3266}
3267
3268static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3269{
3270 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3271
3272 if ( geom.isNull() )
3273 return QVariant();
3274
3275 bool ignoreClosing = false;
3276 if ( values.length() > 1 )
3277 {
3278 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3279 }
3280
3281 QgsMultiPoint *mp = new QgsMultiPoint();
3282
3283 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3284 for ( const QgsRingSequence &part : sequence )
3285 {
3286 for ( const QgsPointSequence &ring : part )
3287 {
3288 bool skipLast = false;
3289 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3290 {
3291 skipLast = true;
3292 }
3293
3294 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
3295 {
3296 mp->addGeometry( ring.at( i ).clone() );
3297 }
3298 }
3299 }
3300
3301 return QVariant::fromValue( QgsGeometry( mp ) );
3302}
3303
3304static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3305{
3306 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3307
3308 if ( geom.isNull() )
3309 return QVariant();
3310
3311 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3312
3313 //OK, now we have a complete list of segmentized lines from the geometry
3315 for ( QgsLineString *line : linesToProcess )
3316 {
3317 for ( int i = 0; i < line->numPoints() - 1; ++i )
3318 {
3320 segment->setPoints( QgsPointSequence()
3321 << line->pointN( i )
3322 << line->pointN( i + 1 ) );
3323 ml->addGeometry( segment );
3324 }
3325 delete line;
3326 }
3327
3328 return QVariant::fromValue( QgsGeometry( ml ) );
3329}
3330
3331static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3332{
3333 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3334
3335 if ( geom.isNull() )
3336 return QVariant();
3337
3338 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3339 if ( !curvePolygon && geom.isMultipart() )
3340 {
3341 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3342 {
3343 if ( collection->numGeometries() == 1 )
3344 {
3345 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3346 }
3347 }
3348 }
3349
3350 if ( !curvePolygon )
3351 return QVariant();
3352
3353 //idx is 1 based
3354 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3355
3356 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3357 return QVariant();
3358
3359 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3360 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3361 return result;
3362}
3363
3364static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3365{
3366 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3367
3368 if ( geom.isNull() )
3369 return QVariant();
3370
3371 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3372 if ( !collection )
3373 return QVariant();
3374
3375 //idx is 1 based
3376 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3377
3378 if ( idx < 0 || idx >= collection->numGeometries() )
3379 return QVariant();
3380
3381 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3382 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3383 return result;
3384}
3385
3386static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3387{
3388 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3389
3390 if ( geom.isNull() )
3391 return QVariant();
3392
3393 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3394 if ( !boundary )
3395 return QVariant();
3396
3397 return QVariant::fromValue( QgsGeometry( boundary ) );
3398}
3399
3400static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3401{
3402 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3403
3404 if ( geom.isNull() )
3405 return QVariant();
3406
3407 QgsGeometry merged = geom.mergeLines();
3408 if ( merged.isNull() )
3409 return QVariant();
3410
3411 return QVariant::fromValue( merged );
3412}
3413
3414static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3415{
3416 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3417 if ( geom.isNull() )
3418 return QVariant();
3419
3420 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3421 if ( geom2.isNull() )
3422 return QVariant();
3423
3424 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3425 if ( sharedPaths.isNull() )
3426 return QVariant();
3427
3428 return QVariant::fromValue( sharedPaths );
3429}
3430
3431
3432static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3433{
3434 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3435
3436 if ( geom.isNull() )
3437 return QVariant();
3438
3439 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3440
3441 QgsGeometry simplified = geom.simplify( tolerance );
3442 if ( simplified.isNull() )
3443 return QVariant();
3444
3445 return simplified;
3446}
3447
3448static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3449{
3450 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3451
3452 if ( geom.isNull() )
3453 return QVariant();
3454
3455 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3456
3458
3459 QgsGeometry simplified = simplifier.simplify( geom );
3460 if ( simplified.isNull() )
3461 return QVariant();
3462
3463 return simplified;
3464}
3465
3466static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3467{
3468 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3469
3470 if ( geom.isNull() )
3471 return QVariant();
3472
3473 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3474 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3475 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3476 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3477
3478 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3479 if ( smoothed.isNull() )
3480 return QVariant();
3481
3482 return smoothed;
3483}
3484
3485static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3486{
3487 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3488
3489 if ( geom.isNull() )
3490 return QVariant();
3491
3492 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3493 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3494 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3495
3496 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3497 if ( waved.isNull() )
3498 return QVariant();
3499
3500 return waved;
3501}
3502
3503static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3504{
3505 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3506
3507 if ( geom.isNull() )
3508 return QVariant();
3509
3510 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3511 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3512 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3513 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3514 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3515
3516 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3517 minAmplitude, maxAmplitude, seed );
3518 if ( waved.isNull() )
3519 return QVariant();
3520
3521 return waved;
3522}
3523
3524static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3525{
3526 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3527
3528 if ( geom.isNull() )
3529 return QVariant();
3530
3531 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3532 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3533 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3534
3535 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3536 if ( waved.isNull() )
3537 return QVariant();
3538
3539 return waved;
3540}
3541
3542static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3543{
3544 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3545
3546 if ( geom.isNull() )
3547 return QVariant();
3548
3549 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3550 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3551 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3552 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3553 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3554
3555 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3556 minAmplitude, maxAmplitude, seed );
3557 if ( waved.isNull() )
3558 return QVariant();
3559
3560 return waved;
3561}
3562
3563static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3564{
3565 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3566
3567 if ( geom.isNull() )
3568 return QVariant();
3569
3570 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3571 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3572 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3573
3574 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3575 if ( waved.isNull() )
3576 return QVariant();
3577
3578 return waved;
3579}
3580
3581static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3582{
3583 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3584
3585 if ( geom.isNull() )
3586 return QVariant();
3587
3588 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3589 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3590 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3591 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3592 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3593
3594 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
3595 minAmplitude, maxAmplitude, seed );
3596 if ( waved.isNull() )
3597 return QVariant();
3598
3599 return waved;
3600}
3601
3602static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3603{
3604 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3605
3606 if ( geom.isNull() )
3607 return QVariant();
3608
3609 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
3610 QVector< double > dashPattern;
3611 dashPattern.reserve( pattern.size() );
3612 for ( const QVariant &value : std::as_const( pattern ) )
3613 {
3614 bool ok = false;
3615 double v = value.toDouble( &ok );
3616 if ( ok )
3617 {
3618 dashPattern << v;
3619 }
3620 else
3621 {
3622 parent->setEvalErrorString( QStringLiteral( "Dash pattern must be an array of numbers" ) );
3623 return QgsGeometry();
3624 }
3625 }
3626
3627 if ( dashPattern.size() % 2 != 0 )
3628 {
3629 parent->setEvalErrorString( QStringLiteral( "Dash pattern must contain an even number of elements" ) );
3630 return QgsGeometry();
3631 }
3632
3633 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
3635 if ( startRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3637 else if ( startRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3639 else if ( startRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3641 else if ( startRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3643 else if ( startRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3645 else
3646 {
3647 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( startRuleString ) );
3648 return QgsGeometry();
3649 }
3650
3651 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3653 if ( endRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3655 else if ( endRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3657 else if ( endRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3659 else if ( endRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3661 else if ( endRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3663 else
3664 {
3665 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( endRuleString ) );
3666 return QgsGeometry();
3667 }
3668
3669 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3671 if ( adjustString.compare( QLatin1String( "both" ), Qt::CaseInsensitive ) == 0 )
3673 else if ( adjustString.compare( QLatin1String( "dash" ), Qt::CaseInsensitive ) == 0 )
3675 else if ( adjustString.compare( QLatin1String( "gap" ), Qt::CaseInsensitive ) == 0 )
3677 else
3678 {
3679 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern size adjustment" ).arg( adjustString ) );
3680 return QgsGeometry();
3681 }
3682
3683 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3684
3685 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
3686 if ( result.isNull() )
3687 return QVariant();
3688
3689 return result;
3690}
3691
3692static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3693{
3694 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3695
3696 if ( geom.isNull() )
3697 return QVariant();
3698
3699 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3700 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
3701 if ( densified.isNull() )
3702 return QVariant();
3703
3704 return densified;
3705}
3706
3707static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3708{
3709 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3710
3711 if ( geom.isNull() )
3712 return QVariant();
3713
3714 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3715 const QgsGeometry densified = geom.densifyByDistance( distance );
3716 if ( densified.isNull() )
3717 return QVariant();
3718
3719 return densified;
3720}
3721
3722static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3723{
3724 QVariantList list;
3725 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
3726 {
3727 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
3728 }
3729 else
3730 {
3731 list = values;
3732 }
3733
3734 QVector< QgsGeometry > parts;
3735 parts.reserve( list.size() );
3736 for ( const QVariant &value : std::as_const( list ) )
3737 {
3738 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
3739 if ( part.isNull() )
3740 return QgsGeometry();
3741 parts << part;
3742 }
3743
3744 return QgsGeometry::collectGeometry( parts );
3745}
3746
3747static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3748{
3749 if ( values.count() < 2 || values.count() > 4 )
3750 {
3751 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
3752 return QVariant();
3753 }
3754
3755 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3756 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3757 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
3758 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
3759 switch ( values.count() )
3760 {
3761 case 2:
3762 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
3763 case 3:
3764 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
3765 case 4:
3766 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
3767 }
3768 return QVariant(); //avoid warning
3769}
3770
3771static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3772{
3773 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3774 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3775 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3776 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
3777}
3778
3779static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3780{
3781 if ( values.empty() )
3782 {
3783 return QVariant();
3784 }
3785
3786 QVector<QgsPoint> points;
3787 points.reserve( values.count() );
3788
3789 auto addPoint = [&points]( const QgsGeometry & geom )
3790 {
3791 if ( geom.isNull() )
3792 return;
3793
3794 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3795 return;
3796
3797 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3798 if ( !point )
3799 return;
3800
3801 points << *point;
3802 };
3803
3804 for ( const QVariant &value : values )
3805 {
3806 if ( value.userType() == QMetaType::Type::QVariantList )
3807 {
3808 const QVariantList list = value.toList();
3809 for ( const QVariant &v : list )
3810 {
3811 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
3812 }
3813 }
3814 else
3815 {
3816 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
3817 }
3818 }
3819
3820 if ( points.count() < 2 )
3821 return QVariant();
3822
3823 return QgsGeometry( new QgsLineString( points ) );
3824}
3825
3826static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3827{
3828 if ( values.count() < 1 )
3829 {
3830 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
3831 return QVariant();
3832 }
3833
3834 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3835
3836 if ( outerRing.type() == Qgis::GeometryType::Polygon )
3837 return outerRing; // if it's already a polygon we have nothing to do
3838
3839 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
3840 return QVariant();
3841
3842 auto polygon = std::make_unique< QgsPolygon >();
3843
3844 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
3845 if ( !exteriorRing && outerRing.isMultipart() )
3846 {
3847 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
3848 {
3849 if ( collection->numGeometries() == 1 )
3850 {
3851 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
3852 }
3853 }
3854 }
3855
3856 if ( !exteriorRing )
3857 return QVariant();
3858
3859 polygon->setExteriorRing( exteriorRing->segmentize() );
3860
3861
3862 for ( int i = 1; i < values.count(); ++i )
3863 {
3864 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
3865 if ( ringGeom.isNull() )
3866 continue;
3867
3868 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
3869 continue;
3870
3871 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
3872 if ( !ring && ringGeom.isMultipart() )
3873 {
3874 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
3875 {
3876 if ( collection->numGeometries() == 1 )
3877 {
3878 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
3879 }
3880 }
3881 }
3882
3883 if ( !ring )
3884 continue;
3885
3886 polygon->addInteriorRing( ring->segmentize() );
3887 }
3888
3889 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
3890}
3891
3892static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3893{
3894 auto tr = std::make_unique<QgsTriangle>();
3895 auto lineString = std::make_unique<QgsLineString>();
3896 lineString->clear();
3897
3898 for ( const QVariant &value : values )
3899 {
3900 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
3901 if ( geom.isNull() )
3902 return QVariant();
3903
3904 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3905 return QVariant();
3906
3907 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3908 if ( !point && geom.isMultipart() )
3909 {
3910 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3911 {
3912 if ( collection->numGeometries() == 1 )
3913 {
3914 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3915 }
3916 }
3917 }
3918
3919 if ( !point )
3920 return QVariant();
3921
3922 lineString->addVertex( *point );
3923 }
3924
3925 tr->setExteriorRing( lineString.release() );
3926
3927 return QVariant::fromValue( QgsGeometry( tr.release() ) );
3928}
3929
3930static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3931{
3932 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3933 if ( geom.isNull() )
3934 return QVariant();
3935
3936 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3937 return QVariant();
3938
3939 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3940 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3941
3942 if ( segment < 3 )
3943 {
3944 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3945 return QVariant();
3946 }
3947 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3948 if ( !point && geom.isMultipart() )
3949 {
3950 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3951 {
3952 if ( collection->numGeometries() == 1 )
3953 {
3954 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3955 }
3956 }
3957 }
3958 if ( !point )
3959 return QVariant();
3960
3961 QgsCircle circ( *point, radius );
3962 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3963}
3964
3965static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3966{
3967 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3968 if ( geom.isNull() )
3969 return QVariant();
3970
3971 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3972 return QVariant();
3973
3974 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3975 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3976 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3977 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
3978 if ( segment < 3 )
3979 {
3980 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3981 return QVariant();
3982 }
3983 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3984 if ( !point && geom.isMultipart() )
3985 {
3986 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3987 {
3988 if ( collection->numGeometries() == 1 )
3989 {
3990 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3991 }
3992 }
3993 }
3994 if ( !point )
3995 return QVariant();
3996
3997 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
3998 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3999}
4000
4001static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4002{
4003
4004 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4005 if ( pt1.isNull() )
4006 return QVariant();
4007
4008 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4009 return QVariant();
4010
4011 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4012 if ( pt2.isNull() )
4013 return QVariant();
4014
4015 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4016 return QVariant();
4017
4018 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4019 if ( nbEdges < 3 )
4020 {
4021 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4022 return QVariant();
4023 }
4024
4025 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4027 {
4028 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4029 return QVariant();
4030 }
4031
4032 const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
4033 if ( !center && pt1.isMultipart() )
4034 {
4035 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
4036 {
4037 if ( collection->numGeometries() == 1 )
4038 {
4039 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4040 }
4041 }
4042 }
4043 if ( !center )
4044 return QVariant();
4045
4046 const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
4047 if ( !corner && pt2.isMultipart() )
4048 {
4049 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
4050 {
4051 if ( collection->numGeometries() == 1 )
4052 {
4053 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4054 }
4055 }
4056 }
4057 if ( !corner )
4058 return QVariant();
4059
4060 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4061
4062 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4063
4064}
4065
4066static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4067{
4068 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4069 if ( pt1.isNull() )
4070 return QVariant();
4071 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4072 return QVariant();
4073
4074 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4075 if ( pt2.isNull() )
4076 return QVariant();
4077 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4078 return QVariant();
4079
4080 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4081 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4082 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4083
4084 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4085}
4086
4087static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4088{
4089 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4090 if ( pt1.isNull() )
4091 return QVariant();
4092 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4093 return QVariant();
4094
4095 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4096 if ( pt2.isNull() )
4097 return QVariant();
4098 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4099 return QVariant();
4100
4101 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4102 if ( pt3.isNull() )
4103 return QVariant();
4104 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4105 return QVariant();
4106
4107 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4108 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4109 {
4110 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4111 return QVariant();
4112 }
4113 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4114 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4115 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4116 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4117 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4118}
4119
4120static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4121{
4122 if ( geom.isNull() )
4123 return QVariant();
4124
4125 if ( idx < 0 )
4126 {
4127 idx += geom.constGet()->nCoordinates();
4128 }
4129 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4130 {
4131 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4132 return QVariant();
4133 }
4134 return QVariant::fromValue( geom.vertexAt( idx ) );
4135}
4136
4137// function used for the old $ style
4138static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4139{
4140 FEAT_FROM_CONTEXT( context, feature )
4141 const QgsGeometry geom = feature.geometry();
4142 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4143
4144 const QVariant v = pointAt( geom, idx, parent );
4145
4146 if ( !v.isNull() )
4147 return QVariant( v.value<QgsPoint>().x() );
4148 else
4149 return QVariant();
4150}
4151static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4152{
4153 if ( values.at( 1 ).isNull() && !values.at( 0 ).isNull() ) // the case where the alias x_at function is called like a $ function (x_at(i))
4154 {
4155 return fcnOldXat( values, f, parent, node );
4156 }
4157 else if ( values.at( 0 ).isNull() && !values.at( 1 ).isNull() ) // same as above with x_at(i:=0) (vertex value is at the second position)
4158 {
4159 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4160 }
4161
4162 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4163 if ( geom.isNull() )
4164 {
4165 return QVariant();
4166 }
4167
4168 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4169
4170 const QVariant v = pointAt( geom, vertexNumber, parent );
4171 if ( !v.isNull() )
4172 return QVariant( v.value<QgsPoint>().x() );
4173 else
4174 return QVariant();
4175}
4176
4177// function used for the old $ style
4178static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4179{
4180 FEAT_FROM_CONTEXT( context, feature )
4181 const QgsGeometry geom = feature.geometry();
4182 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4183
4184 const QVariant v = pointAt( geom, idx, parent );
4185
4186 if ( !v.isNull() )
4187 return QVariant( v.value<QgsPoint>().y() );
4188 else
4189 return QVariant();
4190}
4191static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4192{
4193 if ( values.at( 1 ).isNull() && !values.at( 0 ).isNull() ) // the case where the alias y_at function is called like a $ function (y_at(i))
4194 {
4195 return fcnOldYat( values, f, parent, node );
4196 }
4197 else if ( values.at( 0 ).isNull() && !values.at( 1 ).isNull() ) // same as above with x_at(i:=0) (vertex value is at the second position)
4198 {
4199 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4200 }
4201
4202 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4203 if ( geom.isNull() )
4204 {
4205 return QVariant();
4206 }
4207
4208 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4209
4210 const QVariant v = pointAt( geom, vertexNumber, parent );
4211 if ( !v.isNull() )
4212 return QVariant( v.value<QgsPoint>().y() );
4213 else
4214 return QVariant();
4215}
4216
4217static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4218{
4219 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4220 if ( geom.isNull() )
4221 {
4222 return QVariant();
4223 }
4224
4225 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4226
4227 const QVariant v = pointAt( geom, vertexNumber, parent );
4228 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4229 return QVariant( v.value<QgsPoint>().z() );
4230 else
4231 return QVariant();
4232}
4233
4234static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4235{
4236 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4237 if ( geom.isNull() )
4238 {
4239 return QVariant();
4240 }
4241
4242 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4243
4244 const QVariant v = pointAt( geom, vertexNumber, parent );
4245 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4246 return QVariant( v.value<QgsPoint>().m() );
4247 else
4248 return QVariant();
4249}
4250
4251
4252static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4253{
4254 if ( !context )
4255 return QVariant();
4256
4257 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4258 if ( context->hasGeometry() )
4259 return context->geometry();
4260 else
4261 {
4262 FEAT_FROM_CONTEXT( context, f )
4263 QgsGeometry geom = f.geometry();
4264 if ( !geom.isNull() )
4265 return QVariant::fromValue( geom );
4266 else
4267 return QVariant();
4268 }
4269}
4270
4271static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4272{
4273 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4274 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4275 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4276 return result;
4277}
4278
4279static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4280{
4281 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4282 if ( wkb.isNull() )
4283 return QVariant();
4284
4285 QgsGeometry geom;
4286 geom.fromWkb( wkb );
4287 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4288}
4289
4290static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4291{
4292 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4293 QgsOgcUtils::Context ogcContext;
4294 if ( context )
4295 {
4296 QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
4297 if ( mapLayerPtr )
4298 {
4299 ogcContext.layer = mapLayerPtr.data();
4300 ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
4301 }
4302 }
4303 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4304 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4305 return result;
4306}
4307
4308static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4309{
4310 FEAT_FROM_CONTEXT( context, f )
4312 QgsDistanceArea *calc = parent->geomCalculator();
4313 if ( calc )
4314 {
4315 try
4316 {
4317 double area = calc->measureArea( f.geometry() );
4318 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4319 return QVariant( area );
4320 }
4321 catch ( QgsCsException & )
4322 {
4323 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4324 return QVariant();
4325 }
4326 }
4327 else
4328 {
4329 return QVariant( f.geometry().area() );
4330 }
4331}
4332
4333static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4334{
4335 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4336
4337 if ( geom.type() != Qgis::GeometryType::Polygon )
4338 return QVariant();
4339
4340 return QVariant( geom.area() );
4341}
4342
4343static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4344{
4345 FEAT_FROM_CONTEXT( context, f )
4347 QgsDistanceArea *calc = parent->geomCalculator();
4348 if ( calc )
4349 {
4350 try
4351 {
4352 double len = calc->measureLength( f.geometry() );
4353 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4354 return QVariant( len );
4355 }
4356 catch ( QgsCsException & )
4357 {
4358 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4359 return QVariant();
4360 }
4361 }
4362 else
4363 {
4364 return QVariant( f.geometry().length() );
4365 }
4366}
4367
4368static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4369{
4370 FEAT_FROM_CONTEXT( context, f )
4372 QgsDistanceArea *calc = parent->geomCalculator();
4373 if ( calc )
4374 {
4375 try
4376 {
4377 double len = calc->measurePerimeter( f.geometry() );
4378 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4379 return QVariant( len );
4380 }
4381 catch ( QgsCsException & )
4382 {
4383 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4384 return QVariant();
4385 }
4386 }
4387 else
4388 {
4389 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4390 }
4391}
4392
4393static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4394{
4395 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4396
4397 if ( geom.type() != Qgis::GeometryType::Polygon )
4398 return QVariant();
4399
4400 //length for polygons = perimeter
4401 return QVariant( geom.length() );
4402}
4403
4404static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4405{
4406 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4407 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4408}
4409
4410static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4411{
4412 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4413 if ( geom.isNull() )
4414 return QVariant();
4415
4416 return QVariant( geom.constGet()->partCount() );
4417}
4418
4419static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4420{
4421 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4422 if ( geom.isNull() )
4423 return QVariant();
4424
4425 return QVariant( geom.isMultipart() );
4426}
4427
4428static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4429{
4430 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4431
4432 if ( geom.isNull() )
4433 return QVariant();
4434
4435 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4436 if ( curvePolygon )
4437 return QVariant( curvePolygon->numInteriorRings() );
4438
4439 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4440 if ( collection )
4441 {
4442 //find first CurvePolygon in collection
4443 for ( int i = 0; i < collection->numGeometries(); ++i )
4444 {
4445 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4446 if ( !curvePolygon )
4447 continue;
4448
4449 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4450 }
4451 }
4452
4453 return QVariant();
4454}
4455
4456static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4457{
4458 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4459
4460 if ( geom.isNull() )
4461 return QVariant();
4462
4463 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4464 if ( curvePolygon )
4465 return QVariant( curvePolygon->ringCount() );
4466
4467 bool foundPoly = false;
4468 int ringCount = 0;
4469 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4470 if ( collection )
4471 {
4472 //find CurvePolygons in collection
4473 for ( int i = 0; i < collection->numGeometries(); ++i )
4474 {
4475 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4476 if ( !curvePolygon )
4477 continue;
4478
4479 foundPoly = true;
4480 ringCount += curvePolygon->ringCount();
4481 }
4482 }
4483
4484 if ( !foundPoly )
4485 return QVariant();
4486
4487 return QVariant( ringCount );
4488}
4489
4490static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4491{
4492 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4493 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4494 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4495 return result;
4496}
4497
4498static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4499{
4500 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4501 return QVariant::fromValue( geom.boundingBox().width() );
4502}
4503
4504static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4505{
4506 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4507 return QVariant::fromValue( geom.boundingBox().height() );
4508}
4509
4510static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4511{
4512 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4513 if ( geom.isNull() )
4514 return QVariant();
4515
4517}
4518
4519static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4520{
4521 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4522 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4523}
4524
4525static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4526{
4527 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4528 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4529}
4530
4531static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4532{
4533 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4534 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4535}
4536
4537static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4538{
4539 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4540 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4541}
4542
4543static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4544{
4545 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4546
4547 if ( geom.isNull() || geom.isEmpty( ) )
4548 return QVariant();
4549
4550 if ( !geom.constGet()->is3D() )
4551 return QVariant();
4552
4553 double max = std::numeric_limits< double >::lowest();
4554
4555 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4556 {
4557 double z = ( *it ).z();
4558
4559 if ( max < z )
4560 max = z;
4561 }
4562
4563 if ( max == std::numeric_limits< double >::lowest() )
4564 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4565
4566 return QVariant( max );
4567}
4568
4569static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4570{
4571 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4572
4573 if ( geom.isNull() || geom.isEmpty() )
4574 return QVariant();
4575
4576 if ( !geom.constGet()->is3D() )
4577 return QVariant();
4578
4579 double min = std::numeric_limits< double >::max();
4580
4581 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4582 {
4583 double z = ( *it ).z();
4584
4585 if ( z < min )
4586 min = z;
4587 }
4588
4589 if ( min == std::numeric_limits< double >::max() )
4590 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4591
4592 return QVariant( min );
4593}
4594
4595static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4596{
4597 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4598
4599 if ( geom.isNull() || geom.isEmpty() )
4600 return QVariant();
4601
4602 if ( !geom.constGet()->isMeasure() )
4603 return QVariant();
4604
4605 double min = std::numeric_limits< double >::max();
4606
4607 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4608 {
4609 double m = ( *it ).m();
4610
4611 if ( m < min )
4612 min = m;
4613 }
4614
4615 if ( min == std::numeric_limits< double >::max() )
4616 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4617
4618 return QVariant( min );
4619}
4620
4621static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4622{
4623 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4624
4625 if ( geom.isNull() || geom.isEmpty() )
4626 return QVariant();
4627
4628 if ( !geom.constGet()->isMeasure() )
4629 return QVariant();
4630
4631 double max = std::numeric_limits< double >::lowest();
4632
4633 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4634 {
4635 double m = ( *it ).m();
4636
4637 if ( max < m )
4638 max = m;
4639 }
4640
4641 if ( max == std::numeric_limits< double >::lowest() )
4642 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4643
4644 return QVariant( max );
4645}
4646
4647static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4648{
4649 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4650 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom.constGet() );
4651 if ( !curve )
4652 {
4653 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
4654 return QVariant();
4655 }
4656
4657 return QVariant( curve->sinuosity() );
4658}
4659
4660static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4661{
4662 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4663 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4664 if ( !curve )
4665 {
4666 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
4667 return QVariant();
4668 }
4669
4670 return QVariant( curve->straightDistance2d() );
4671}
4672
4673static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4674{
4675 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4676 const QgsCurvePolygon *poly = geom.constGet() ? qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4677
4678 if ( !poly )
4679 {
4680 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
4681 return QVariant();
4682 }
4683
4684 return QVariant( poly->roundness() );
4685}
4686
4687
4688
4689static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4690{
4691 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4692 if ( geom.isNull() )
4693 return QVariant();
4694
4695 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
4696 flipped->swapXy();
4697 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
4698}
4699
4700static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4701{
4702 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4703 if ( fGeom.isNull() )
4704 return QVariant();
4705
4706 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
4707 if ( !curve && fGeom.isMultipart() )
4708 {
4709 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4710 {
4711 if ( collection->numGeometries() == 1 )
4712 {
4713 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4714 }
4715 }
4716 }
4717
4718 if ( !curve )
4719 return QVariant();
4720
4721 return QVariant::fromValue( curve->isClosed() );
4722}
4723
4724static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4725{
4726 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4727
4728 if ( geom.isNull() )
4729 return QVariant();
4730
4731 QVariant result;
4732 if ( !geom.isMultipart() )
4733 {
4734 const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
4735
4736 if ( !line )
4737 return QVariant();
4738
4739 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4740 closedLine->close();
4741
4742 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
4743 }
4744 else
4745 {
4746 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
4747 if ( !collection )
4748 return QVariant();
4749
4750 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
4751
4752 for ( int i = 0; i < collection->numGeometries(); ++i )
4753 {
4754 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
4755 {
4756 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4757 closedLine->close();
4758
4759 closed->addGeometry( closedLine.release() );
4760 }
4761 }
4762 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
4763 }
4764
4765 return result;
4766}
4767
4768static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4769{
4770 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4771 if ( fGeom.isNull() )
4772 return QVariant();
4773
4774 return QVariant::fromValue( fGeom.isEmpty() );
4775}
4776
4777static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4778{
4779 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
4780 return QVariant::fromValue( true );
4781
4782 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4783 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
4784}
4785
4786static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4787{
4788 if ( values.length() < 2 || values.length() > 3 )
4789 return QVariant();
4790
4791 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4792 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4793
4794 if ( fGeom.isNull() || sGeom.isNull() )
4795 return QVariant();
4796
4797 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
4798
4799 if ( values.length() == 2 )
4800 {
4801 //two geometry arguments, return relation
4802 QString result = engine->relate( sGeom.constGet() );
4803 return QVariant::fromValue( result );
4804 }
4805 else
4806 {
4807 //three arguments, test pattern
4808 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4809 bool result = engine->relatePattern( sGeom.constGet(), pattern );
4810 return QVariant::fromValue( result );
4811 }
4812}
4813
4814static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4815{
4816 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4817 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4818 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
4819}
4820static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4821{
4822 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4823 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4824 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
4825}
4826static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4827{
4828 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4829 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4830 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
4831}
4832static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4833{
4834 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4835 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4836 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
4837}
4838static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4839{
4840 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4841 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4842 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
4843}
4844static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4845{
4846 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4847 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4848 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
4849}
4850static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4851{
4852 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4853 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4854 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
4855}
4856static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4857{
4858 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4859 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4860 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
4861}
4862
4863static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4864{
4865 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4866 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4867 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4868 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4869 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4870 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4871
4873 if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
4874 capStyle = Qgis::EndCapStyle::Flat;
4875 else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
4876 capStyle = Qgis::EndCapStyle::Square;
4877
4879 if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
4880 joinStyle = Qgis::JoinStyle::Miter;
4881 else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
4882 joinStyle = Qgis::JoinStyle::Bevel;
4883
4884 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
4885 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4886 return result;
4887}
4888
4889static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4890{
4891 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4892 const QgsGeometry reoriented = fGeom.forceRHR();
4893 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4894}
4895
4896static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4897{
4898 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4899 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
4900 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4901}
4902
4903static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4904{
4905 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4906 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
4907 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4908}
4909
4910static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4911{
4912 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4913 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
4914 if ( !pt && fGeom.isMultipart() )
4915 {
4916 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4917 {
4918 if ( collection->numGeometries() == 1 )
4919 {
4920 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4921 }
4922 }
4923 }
4924
4925 if ( !pt )
4926 {
4927 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
4928 return QVariant();
4929 }
4930
4931 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4932 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4933 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4934 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4935
4936 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
4937 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4938 return result;
4939}
4940
4941static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4942{
4943 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4944 if ( fGeom.type() != Qgis::GeometryType::Line )
4945 {
4946 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
4947 return QVariant();
4948 }
4949
4950 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4951 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4952 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4953
4954 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
4955 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4956 return result;
4957}
4958
4959static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4960{
4961 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4962 if ( fGeom.type() != Qgis::GeometryType::Line )
4963 {
4964 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
4965 return QVariant();
4966 }
4967
4968 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
4969
4970 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
4971 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4972 return result;
4973}
4974
4975static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4976{
4977 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4978 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4979 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4980 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4981 if ( joinInt < 1 || joinInt > 3 )
4982 return QVariant();
4983 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4984
4985 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4986
4987 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
4988 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4989 return result;
4990}
4991
4992static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4993{
4994 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4995 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4996 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4997
4998 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4999 if ( joinInt < 1 || joinInt > 3 )
5000 return QVariant();
5001 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5002
5003 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5004
5005 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5006 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5007 return result;
5008}
5009
5010static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5011{
5012 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5013 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5014 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5015
5016 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
5017 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5018 return result;
5019}
5020
5021static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5022{
5023 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5024 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5025 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5026 fGeom.translate( dx, dy );
5027 return QVariant::fromValue( fGeom );
5028}
5029
5030static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5031{
5032 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5033 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5034 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
5035 : QgsGeometry();
5036 const bool perPart = values.value( 3 ).toBool();
5037
5038 if ( center.isNull() && perPart && fGeom.isMultipart() )
5039 {
5040 // no explicit center, rotating per part
5041 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5042 // the result is equivalent to setting perPart as false anyway)
5043 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5044 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5045 {
5046 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5047 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5048 t.rotate( -rotation );
5049 t.translate( -partCenter.x(), -partCenter.y() );
5050 ( *it )->transform( t );
5051 }
5052 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5053 }
5054 else
5055 {
5056 QgsPointXY pt;
5057 if ( center.isEmpty() )
5058 {
5059 // if center wasn't specified, use bounding box centroid
5060 pt = fGeom.boundingBox().center();
5061 }
5063 {
5064 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5065 return QVariant();
5066 }
5067 else
5068 {
5069 pt = QgsPointXY( *qgsgeometry_cast< const QgsPoint * >( center.constGet()->simplifiedTypeRef() ) );
5070 }
5071
5072 fGeom.rotate( rotation, pt );
5073 return QVariant::fromValue( fGeom );
5074 }
5075}
5076
5077static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5078{
5079 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5080 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5081 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5082 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
5083 : QgsGeometry();
5084
5085 QgsPointXY pt;
5086 if ( center.isNull() )
5087 {
5088 // if center wasn't specified, use bounding box centroid
5089 pt = fGeom.boundingBox().center();
5090 }
5092 {
5093 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5094 return QVariant();
5095 }
5096 else
5097 {
5098 pt = center.asPoint();
5099 }
5100
5101 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5102 t.scale( xScale, yScale );
5103 t.translate( -pt.x(), -pt.y() );
5104 fGeom.transform( t );
5105 return QVariant::fromValue( fGeom );
5106}
5107
5108static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5109{
5110 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5111 if ( fGeom.isNull() )
5112 {
5113 return QVariant();
5114 }
5115
5116 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5117 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5118
5119 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5120
5121 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5122 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5123
5124 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5125 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5126 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5127 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5128
5129 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5130 {
5131 fGeom.get()->addZValue( 0 );
5132 }
5133 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5134 {
5135 fGeom.get()->addMValue( 0 );
5136 }
5137
5138 QTransform transform;
5139 transform.translate( deltaX, deltaY );
5140 transform.rotate( rotationZ );
5141 transform.scale( scaleX, scaleY );
5142 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5143
5144 return QVariant::fromValue( fGeom );
5145}
5146
5147
5148static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5149{
5150 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5151 QgsGeometry geom = fGeom.centroid();
5152 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5153 return result;
5154}
5155static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5156{
5157 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5158 QgsGeometry geom = fGeom.pointOnSurface();
5159 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5160 return result;
5161}
5162
5163static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5164{
5165 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5166 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5167 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5168 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5169 return result;
5170}
5171
5172static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5173{
5174 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5175 QgsGeometry geom = fGeom.convexHull();
5176 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5177 return result;
5178}
5179
5180#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
5181static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5182{
5183 try
5184 {
5185 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5186 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5187 const bool allowHoles = values.value( 2 ).toBool();
5188 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5189 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5190 return result;
5191 }
5192 catch ( QgsCsException &cse )
5193 {
5194 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5195 return QVariant();
5196 }
5197}
5198#endif
5199
5200static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5201{
5202 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5203 int segments = 36;
5204 if ( values.length() == 2 )
5205 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5206 if ( segments < 0 )
5207 {
5208 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5209 return QVariant();
5210 }
5211
5212 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5213 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5214 return result;
5215}
5216
5217static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5218{
5219 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5221 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5222 return result;
5223}
5224
5225static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5226{
5227 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5228
5229 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5230 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5231 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5232
5233 double area, angle, width, height;
5234 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5235
5236 if ( geom.isNull() )
5237 {
5238 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5239 return QVariant();
5240 }
5241 return angle;
5242}
5243
5244static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5245{
5246 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5247 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5248 QgsGeometry geom = fGeom.difference( sGeom );
5249 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5250 return result;
5251}
5252
5253static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5254{
5255 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5256 if ( fGeom.isNull() )
5257 return QVariant();
5258
5259 QVariant result;
5260 if ( !fGeom.isMultipart() )
5261 {
5262 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5263 if ( !curve )
5264 return QVariant();
5265
5266 QgsCurve *reversed = curve->reversed();
5267 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5268 }
5269 else
5270 {
5271 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
5272 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5273 for ( int i = 0; i < collection->numGeometries(); ++i )
5274 {
5275 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5276 {
5277 reversed->addGeometry( curve->reversed() );
5278 }
5279 else
5280 {
5281 reversed->addGeometry( collection->geometryN( i )->clone() );
5282 }
5283 }
5284 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5285 }
5286 return result;
5287}
5288
5289static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5290{
5291 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5292 if ( fGeom.isNull() )
5293 return QVariant();
5294
5295 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
5296 if ( !curvePolygon && fGeom.isMultipart() )
5297 {
5298 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
5299 {
5300 if ( collection->numGeometries() == 1 )
5301 {
5302 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5303 }
5304 }
5305 }
5306
5307 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5308 return QVariant();
5309
5310 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5311 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5312 return result;
5313}
5314
5315static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5316{
5317 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5318 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5319 return QVariant( fGeom.distance( sGeom ) );
5320}
5321
5322static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5323{
5324 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5325 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5326
5327 double res = -1;
5328 if ( values.length() == 3 && values.at( 2 ).isValid() )
5329 {
5330 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5331 densify = std::clamp( densify, 0.0, 1.0 );
5332 res = g1.hausdorffDistanceDensify( g2, densify );
5333 }
5334 else
5335 {
5336 res = g1.hausdorffDistance( g2 );
5337 }
5338
5339 return res > -1 ? QVariant( res ) : QVariant();
5340}
5341
5342static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5343{
5344 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5345 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5346 QgsGeometry geom = fGeom.intersection( sGeom );
5347 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5348 return result;
5349}
5350static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5351{
5352 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5353 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5354 QgsGeometry geom = fGeom.symDifference( sGeom );
5355 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5356 return result;
5357}
5358static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5359{
5360 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5361 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5362 QgsGeometry geom = fGeom.combine( sGeom );
5363 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5364 return result;
5365}
5366
5367static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5368{
5369 if ( values.length() < 1 || values.length() > 2 )
5370 return QVariant();
5371
5372 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5373 int prec = 8;
5374 if ( values.length() == 2 )
5375 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5376 QString wkt = fGeom.asWkt( prec );
5377 return QVariant( wkt );
5378}
5379
5380static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5381{
5382 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5383 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5384}
5385
5386static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5387{
5388 if ( values.length() != 2 )
5389 {
5390 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5391 return QVariant();
5392 }
5393
5394 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5395 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5396
5397 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5398 if ( !pt1 && fGeom1.isMultipart() )
5399 {
5400 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5401 {
5402 if ( collection->numGeometries() == 1 )
5403 {
5404 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5405 }
5406 }
5407 }
5408
5409 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5410 if ( !pt2 && fGeom2.isMultipart() )
5411 {
5412 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5413 {
5414 if ( collection->numGeometries() == 1 )
5415 {
5416 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5417 }
5418 }
5419 }
5420
5421 if ( !pt1 || !pt2 )
5422 {
5423 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5424 return QVariant();
5425 }
5426
5427 // Code from PostGIS
5428 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5429 {
5430 if ( pt1->y() < pt2->y() )
5431 return 0.0;
5432 else if ( pt1->y() > pt2->y() )
5433 return M_PI;
5434 else
5435 return 0;
5436 }
5437
5438 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5439 {
5440 if ( pt1->x() < pt2->x() )
5441 return M_PI_2;
5442 else if ( pt1->x() > pt2->x() )
5443 return M_PI + ( M_PI_2 );
5444 else
5445 return 0;
5446 }
5447
5448 if ( pt1->x() < pt2->x() )
5449 {
5450 if ( pt1->y() < pt2->y() )
5451 {
5452 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5453 }
5454 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5455 {
5456 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5457 + ( M_PI_2 );
5458 }
5459 }
5460
5461 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5462 {
5463 if ( pt1->y() > pt2->y() )
5464 {
5465 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
5466 + M_PI;
5467 }
5468 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5469 {
5470 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5471 + ( M_PI + ( M_PI_2 ) );
5472 }
5473 }
5474}
5475
5476static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5477{
5478 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5479 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5480 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
5481 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
5482
5483 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
5484 {
5485 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
5486 return QVariant();
5487 }
5488
5489 const QgsPointXY point1 = geom1.asPoint();
5490 const QgsPointXY point2 = geom2.asPoint();
5491 if ( point1.isEmpty() || point2.isEmpty() )
5492 {
5493 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
5494 return QVariant();
5495 }
5496
5498 if ( context )
5499 {
5500 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5501
5502 if ( !sourceCrs.isValid() )
5503 {
5504 sourceCrs = context->variable( QStringLiteral( "_layer_crs" ) ).value<QgsCoordinateReferenceSystem>();
5505 }
5506
5507 if ( ellipsoid.isEmpty() )
5508 {
5509 ellipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
5510 }
5511 }
5512
5513 if ( !sourceCrs.isValid() )
5514 {
5515 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
5516 return QVariant();
5517 }
5518
5519 QgsDistanceArea da;
5520 da.setSourceCrs( sourceCrs, tContext );
5521 if ( !da.setEllipsoid( ellipsoid ) )
5522 {
5523 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
5524 return QVariant();
5525 }
5526
5527 try
5528 {
5529 const double bearing = da.bearing( point1, point2 );
5530 if ( std::isfinite( bearing ) )
5531 {
5532 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
5533 }
5534 }
5535 catch ( QgsCsException &cse )
5536 {
5537 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
5538 return QVariant();
5539 }
5540 return QVariant();
5541}
5542
5543static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5544{
5545 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5546
5548 {
5549 parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
5550 return QVariant();
5551 }
5552
5553 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5554 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5555 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5556
5557 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
5558 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5559
5560 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5561}
5562
5563static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5564{
5565 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5566 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5567
5568 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5569 if ( !pt1 && fGeom1.isMultipart() )
5570 {
5571 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5572 {
5573 if ( collection->numGeometries() == 1 )
5574 {
5575 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5576 }
5577 }
5578 }
5579 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5580 if ( !pt2 && fGeom2.isMultipart() )
5581 {
5582 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5583 {
5584 if ( collection->numGeometries() == 1 )
5585 {
5586 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5587 }
5588 }
5589 }
5590
5591 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) ||
5592 !pt1 || !pt2 )
5593 {
5594 parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
5595 return QVariant();
5596 }
5597
5598 return pt1->inclination( *pt2 );
5599
5600}
5601
5602static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5603{
5604 if ( values.length() != 3 )
5605 return QVariant();
5606
5607 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5608 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5609 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5610
5611 QgsGeometry geom = fGeom.extrude( x, y );
5612
5613 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
5614 return result;
5615}
5616
5617static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
5618{
5619 if ( values.length() < 2 )
5620 return QVariant();
5621
5622 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5623
5624 if ( !fGeom.isMultipart() )
5625 return values.at( 0 );
5626
5627 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5628 QVariant cachedExpression;
5629 if ( ctx )
5630 cachedExpression = ctx->cachedValue( expString );
5631 QgsExpression expression;
5632
5633 if ( cachedExpression.isValid() )
5634 {
5635 expression = cachedExpression.value<QgsExpression>();
5636 }
5637 else
5638 expression = QgsExpression( expString );
5639
5640 bool asc = values.value( 2 ).toBool();
5641
5642 QgsExpressionContext *unconstedContext = nullptr;
5643 QgsFeature f;
5644 if ( ctx )
5645 {
5646 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
5647 // so no reason to worry
5648 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
5649 f = ctx->feature();
5650 }
5651 else
5652 {
5653 // If there's no context provided, create a fake one
5654 unconstedContext = new QgsExpressionContext();
5655 }
5656
5657 const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
5658 Q_ASSERT( collection ); // Should have failed the multipart check above
5659
5661 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
5662 QgsExpressionSorter sorter( orderBy );
5663
5664 QList<QgsFeature> partFeatures;
5665 partFeatures.reserve( collection->partCount() );
5666 for ( int i = 0; i < collection->partCount(); ++i )
5667 {
5668 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
5669 partFeatures << f;
5670 }
5671
5672 sorter.sortFeatures( partFeatures, unconstedContext );
5673
5674 QgsGeometryCollection *orderedGeom = qgsgeometry_cast<QgsGeometryCollection *>( fGeom.constGet()->clone() );
5675
5676 Q_ASSERT( orderedGeom );
5677
5678 while ( orderedGeom->partCount() )
5679 orderedGeom->removeGeometry( 0 );
5680
5681 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
5682 {
5683 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
5684 }
5685
5686 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
5687
5688 if ( !ctx )
5689 delete unconstedContext;
5690
5691 return result;
5692}
5693
5694static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5695{
5696 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5697 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5698
5699 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
5700
5701 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5702 return result;
5703}
5704
5705static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5706{
5707 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5708 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5709
5710 QgsGeometry geom = fromGeom.shortestLine( toGeom );
5711
5712 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5713 return result;
5714}
5715
5716static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5717{
5718 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5719 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5720
5721 QgsGeometry geom = lineGeom.interpolate( distance );
5722
5723 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5724 return result;
5725}
5726
5727static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5728{
5729 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5730 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5731 const bool use3DDistance = values.at( 2 ).toBool();
5732
5733 double x, y, z, distance;
5734
5735 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5736 if ( !line )
5737 {
5738 return QVariant();
5739 }
5740
5741 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
5742 {
5743 QgsPoint point( x, y );
5744 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
5745 {
5746 point.addZValue( z );
5747 }
5748 return QVariant::fromValue( QgsGeometry( point.clone() ) );
5749 }
5750
5751 return QVariant();
5752}
5753
5754static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5755{
5756 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5757 if ( lineGeom.type() != Qgis::GeometryType::Line )
5758 {
5759 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
5760 return QVariant();
5761 }
5762
5763 const QgsCurve *curve = nullptr;
5764 if ( !lineGeom.isMultipart() )
5765 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
5766 else
5767 {
5768 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
5769 {
5770 if ( collection->numGeometries() > 0 )
5771 {
5772 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5773 }
5774 }
5775 }
5776 if ( !curve )
5777 return QVariant();
5778
5779 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5780 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5781
5782 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
5783 QgsGeometry result( std::move( substring ) );
5784 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
5785}
5786
5787static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5788{
5789 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5790 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5791
5792 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
5793}
5794
5795static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5796{
5797 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5798 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5799 if ( vertex < 0 )
5800 {
5801 //negative idx
5802 int count = geom.constGet()->nCoordinates();
5803 vertex = count + vertex;
5804 }
5805
5806 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
5807}
5808
5809static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5810{
5811 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5812 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5813 if ( vertex < 0 )
5814 {
5815 //negative idx
5816 int count = geom.constGet()->nCoordinates();
5817 vertex = count + vertex;
5818 }
5819
5820 return geom.distanceToVertex( vertex );
5821}
5822
5823static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5824{
5825 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5826 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5827
5828 double distance = lineGeom.lineLocatePoint( pointGeom );
5829
5830 return distance >= 0 ? distance : QVariant();
5831}
5832
5833static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5834{
5835 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5836 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5837 const bool use3DDistance = values.at( 2 ).toBool();
5838
5839 double x, y, z, distance;
5840
5841 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5842 if ( !line )
5843 {
5844 return QVariant();
5845 }
5846
5847 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
5848 return found ? distance : QVariant();
5849}
5850
5851static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5852{
5853 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
5854 {
5855 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5856 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5857 }
5858
5859 if ( values.length() >= 1 )
5860 {
5861 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5862 return QVariant( qlonglong( std::round( number ) ) );
5863 }
5864
5865 return QVariant();
5866}
5867
5868static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5869{
5870 Q_UNUSED( values )
5871 Q_UNUSED( parent )
5872 return M_PI;
5873}
5874
5875static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5876{
5877 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5878 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5879 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5880 if ( places < 0 )
5881 {
5882 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
5883 return QVariant();
5884 }
5885
5886 const bool omitGroupSeparator = values.value( 3 ).toBool();
5887 const bool trimTrailingZeros = values.value( 4 ).toBool();
5888
5889 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5890 if ( !omitGroupSeparator )
5891 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
5892 else
5893 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
5894
5895 QString res = locale.toString( value, 'f', places );
5896
5897 if ( trimTrailingZeros )
5898 {
5899#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5900 const QChar decimal = locale.decimalPoint();
5901 const QChar zeroDigit = locale.zeroDigit();
5902#else
5903 const QChar decimal = locale.decimalPoint().at( 0 );
5904 const QChar zeroDigit = locale.zeroDigit().at( 0 );
5905#endif
5906
5907 if ( res.contains( decimal ) )
5908 {
5909 int trimPoint = res.length() - 1;
5910
5911 while ( res.at( trimPoint ) == zeroDigit )
5912 trimPoint--;
5913
5914 if ( res.at( trimPoint ) == decimal )
5915 trimPoint--;
5916
5917 res.truncate( trimPoint + 1 );
5918 }
5919 }
5920
5921 return res;
5922}
5923
5924static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5925{
5926 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
5927 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5928 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5929
5930 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
5931 if ( format.indexOf( "Z" ) > 0 )
5932 datetime = datetime.toUTC();
5933
5934 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5935 return locale.toString( datetime, format );
5936}
5937
5938static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5939{
5940 const QVariant variant = values.at( 0 );
5941 bool isQColor;
5942 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
5943 if ( !color.isValid() )
5944 return QVariant();
5945
5946 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5947 if ( color.spec() == QColor::Spec::Cmyk )
5948 {
5949 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5950 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
5951 }
5952 else
5953 {
5954 const float avg = ( color.redF() + color.greenF() + color.blueF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5955 color.setRgbF( avg, avg, avg, alpha );
5956 }
5957
5958 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
5959}
5960
5961static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5962{
5963 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5964 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
5965 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5966 if ( ratio > 1 )
5967 {
5968 ratio = 1;
5969 }
5970 else if ( ratio < 0 )
5971 {
5972 ratio = 0;
5973 }
5974
5975 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
5976 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
5977 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
5978 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
5979
5980 QColor newColor( red, green, blue, alpha );
5981
5982 return QgsSymbolLayerUtils::encodeColor( newColor );
5983}
5984
5985static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5986{
5987 const QVariant variant1 = values.at( 0 );
5988 const QVariant variant2 = values.at( 1 );
5989
5990 if ( variant1.userType() != variant2.userType() )
5991 {
5992 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
5993 return QVariant();
5994 }
5995
5996 bool isQColor;
5997 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
5998 if ( !color1.isValid() )
5999 return QVariant();
6000
6001 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6002 if ( !color2.isValid() )
6003 return QVariant();
6004
6005 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6006 {
6007 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6008 return QVariant();
6009 }
6010
6011 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6012
6013 // TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
6014 // NOLINTBEGIN(bugprone-narrowing-conversions)
6015
6016 QColor newColor;
6017 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6018 if ( color1.spec() == QColor::Spec::Cmyk )
6019 {
6020 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6021 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6022 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6023 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6024 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6025 }
6026 else
6027 {
6028 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6029 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6030 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6031 newColor = QColor::fromRgbF( red, green, blue, alpha );
6032 }
6033
6034 // NOLINTEND(bugprone-narrowing-conversions)
6035
6036 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6037}
6038
6039static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6040{
6041 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6042 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6043 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6044 QColor color = QColor( red, green, blue );
6045 if ( ! color.isValid() )
6046 {
6047 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6048 color = QColor( 0, 0, 0 );
6049 }
6050
6051 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6052}
6053
6054static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6055{
6056 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6057 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6058 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6059 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6060 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6061 if ( ! color.isValid() )
6062 {
6063 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6064 return QVariant();
6065 }
6066
6067 return color;
6068}
6069
6070static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6071{
6072 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6073 QVariant value = node->eval( parent, context );
6074 if ( parent->hasEvalError() )
6075 {
6076 parent->setEvalErrorString( QString() );
6077 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6079 value = node->eval( parent, context );
6081 }
6082 return value;
6083}
6084
6085static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6086{
6087 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6089 QVariant value = node->eval( parent, context );
6091 if ( value.toBool() )
6092 {
6093 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6095 value = node->eval( parent, context );
6097 }
6098 else
6099 {
6100 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6102 value = node->eval( parent, context );
6104 }
6105 return value;
6106}
6107
6108static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6109{
6110 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6111 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6112 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6113 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6114 QColor color = QColor( red, green, blue, alpha );
6115 if ( ! color.isValid() )
6116 {
6117 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6118 color = QColor( 0, 0, 0 );
6119 }
6120 return QgsSymbolLayerUtils::encodeColor( color );
6121}
6122
6123QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6124{
6125 QgsGradientColorRamp expRamp;
6126 const QgsColorRamp *ramp = nullptr;
6127 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6128 {
6129 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6130 ramp = &expRamp;
6131 }
6132 else
6133 {
6134 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6135 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6136 if ( ! ramp )
6138 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6139 return QVariant();
6140 }
6141 }
6142
6143 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6144 QColor color = ramp->color( value );
6145 return color;
6146}
6147
6148QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6149{
6150 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6151 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6152}
6153
6154static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6155{
6156 // Hue ranges from 0 - 360
6157 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6158 // Saturation ranges from 0 - 100
6159 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6160 // Lightness ranges from 0 - 100
6161 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6162
6163 QColor color = QColor::fromHslF( hue, saturation, lightness );
6164
6165 if ( ! color.isValid() )
6166 {
6167 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6168 color = QColor( 0, 0, 0 );
6169 }
6170
6171 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6172}
6173
6174static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6175{
6176 // Hue ranges from 0 - 360
6177 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6178 // Saturation ranges from 0 - 100
6179 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6180 // Lightness ranges from 0 - 100
6181 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6182 // Alpha ranges from 0 - 255
6183 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6184
6185 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6186 if ( ! color.isValid() )
6187 {
6188 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6189 color = QColor( 0, 0, 0 );
6190 }
6191 return QgsSymbolLayerUtils::encodeColor( color );
6192}
6193
6194static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6195{
6196 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6197 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6198 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6199 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6200
6201 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6202 if ( ! color.isValid() )
6203 {
6204 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6205 return QVariant();
6206 }
6207
6208 return color;
6209}
6210
6211static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6212{
6213 // Hue ranges from 0 - 360
6214 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6215 // Saturation ranges from 0 - 100
6216 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6217 // Value ranges from 0 - 100
6218 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6219
6220 QColor color = QColor::fromHsvF( hue, saturation, value );
6221
6222 if ( ! color.isValid() )
6223 {
6224 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6225 color = QColor( 0, 0, 0 );
6226 }
6227
6228 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6229}
6230
6231static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6232{
6233 // Hue ranges from 0 - 360
6234 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6235 // Saturation ranges from 0 - 100
6236 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6237 // Value ranges from 0 - 100
6238 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6239 // Alpha ranges from 0 - 255
6240 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6241
6242 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6243 if ( ! color.isValid() )
6244 {
6245 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6246 color = QColor( 0, 0, 0 );
6247 }
6248 return QgsSymbolLayerUtils::encodeColor( color );
6249}
6250
6251static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6252{
6253 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6254 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6255 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6256 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6257 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6258
6259 if ( ! color.isValid() )
6260 {
6261 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6262 return QVariant();
6263 }
6264
6265 return color;
6266}
6267
6268static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6269{
6270 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6271 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6272 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6273 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6274 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6275
6276 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6277 if ( ! color.isValid() )
6278 {
6279 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6280 return QVariant();
6281 }
6282
6283 return color;
6284}
6285
6286static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6287{
6288 // Cyan ranges from 0 - 100
6289 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6290 // Magenta ranges from 0 - 100
6291 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6292 // Yellow ranges from 0 - 100
6293 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6294 // Black ranges from 0 - 100
6295 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6296
6297 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6298
6299 if ( ! color.isValid() )
6300 {
6301 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6302 color = QColor( 0, 0, 0 );
6303 }
6304
6305 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6306}
6307
6308static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6309{
6310 // Cyan ranges from 0 - 100
6311 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6312 // Magenta ranges from 0 - 100
6313 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6314 // Yellow ranges from 0 - 100
6315 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6316 // Black ranges from 0 - 100
6317 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6318 // Alpha ranges from 0 - 255
6319 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6320
6321 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6322 if ( ! color.isValid() )
6323 {
6324 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6325 color = QColor( 0, 0, 0 );
6326 }
6327 return QgsSymbolLayerUtils::encodeColor( color );
6328}
6329
6330static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6331{
6332 const QVariant variant = values.at( 0 );
6333 bool isQColor;
6334 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6335 if ( !color.isValid() )
6336 return QVariant();
6337
6338 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6339 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6340 return color.red();
6341 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6342 return color.green();
6343 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6344 return color.blue();
6345 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6346 return color.alpha();
6347 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6348 return static_cast< double >( color.hsvHueF() * 360 );
6349 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6350 return static_cast< double >( color.hsvSaturationF() * 100 );
6351 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6352 return static_cast< double >( color.valueF() * 100 );
6353 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6354 return static_cast< double >( color.hslHueF() * 360 );
6355 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6356 return static_cast< double >( color.hslSaturationF() * 100 );
6357 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6358 return static_cast< double >( color.lightnessF() * 100 );
6359 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6360 return static_cast< double >( color.cyanF() * 100 );
6361 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6362 return static_cast< double >( color.magentaF() * 100 );
6363 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6364 return static_cast< double >( color.yellowF() * 100 );
6365 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6366 return static_cast< double >( color.blackF() * 100 );
6367
6368 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6369 return QVariant();
6370}
6371
6372static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6373{
6374 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6375 if ( map.empty() )
6376 {
6377 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6378 return QVariant();
6379 }
6380
6381 QList< QColor > colors;
6383 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6384 {
6385 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
6386 if ( !colors.last().isValid() )
6387 {
6388 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
6389 return QVariant();
6390 }
6391
6392 double step = it.key().toDouble();
6393 if ( it == map.constBegin() )
6394 {
6395 if ( step != 0.0 )
6396 stops << QgsGradientStop( step, colors.last() );
6397 }
6398 else if ( it == map.constEnd() )
6399 {
6400 if ( step != 1.0 )
6401 stops << QgsGradientStop( step, colors.last() );
6402 }
6403 else
6404 {
6405 stops << QgsGradientStop( step, colors.last() );
6406 }
6407 }
6408 bool discrete = values.at( 1 ).toBool();
6409
6410 if ( colors.empty() )
6411 return QVariant();
6412
6413 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
6414}
6415
6416static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6417{
6418 const QVariant variant = values.at( 0 );
6419 bool isQColor;
6420 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6421 if ( !color.isValid() )
6422 return QVariant();
6423
6424 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6425 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6426 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6427 color.setRed( std::clamp( value, 0, 255 ) );
6428 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6429 color.setGreen( std::clamp( value, 0, 255 ) );
6430 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6431 color.setBlue( std::clamp( value, 0, 255 ) );
6432 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6433 color.setAlpha( std::clamp( value, 0, 255 ) );
6434 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6435 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
6436 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6437 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
6438 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6439 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6440 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6441 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
6442 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6443 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
6444 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6445 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6446 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6447 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
6448 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6449 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
6450 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6451 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
6452 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6453 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6454 else
6455 {
6456 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6457 return QVariant();
6458 }
6459 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6460}
6461
6462static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6463{
6464 const QVariant variant = values.at( 0 );
6465 bool isQColor;
6466 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6467 if ( !color.isValid() )
6468 return QVariant();
6469
6470 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6471
6472 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6473}
6474
6475static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6476{
6477 const QVariant variant = values.at( 0 );
6478 bool isQColor;
6479 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6480 if ( !color.isValid() )
6481 return QVariant();
6482
6483 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6484
6485 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6486}
6487
6488static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6489{
6490 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6491 QgsGeometry geom = feat.geometry();
6492 if ( !geom.isNull() )
6493 return QVariant::fromValue( geom );
6494 return QVariant();
6495}
6496
6497static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6498{
6499 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6500 if ( !feat.isValid() )
6501 return QVariant();
6502 return feat.id();
6503}
6504
6505static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6506{
6507 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6508 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
6509 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6510
6511 if ( !sCrs.isValid() )
6512 return QVariant::fromValue( fGeom );
6513
6514 if ( !dCrs.isValid() )
6515 return QVariant::fromValue( fGeom );
6516
6518 if ( context )
6519 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
6520 QgsCoordinateTransform t( sCrs, dCrs, tContext );
6521 try
6522 {
6524 return QVariant::fromValue( fGeom );
6525 }
6526 catch ( QgsCsException &cse )
6527 {
6528 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
6529 return QVariant();
6530 }
6531 return QVariant();
6532}
6533
6534
6535static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6536{
6537 bool foundLayer = false;
6538 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6539
6540 //no layer found
6541 if ( !featureSource || !foundLayer )
6542 {
6543 return QVariant();
6544 }
6545
6546 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
6547
6549 req.setFilterFid( fid );
6550 req.setTimeout( 10000 );
6551 req.setRequestMayBeNested( true );
6552 if ( context )
6553 req.setFeedback( context->feedback() );
6554 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6555
6556 QgsFeature fet;
6557 QVariant result;
6558 if ( fIt.nextFeature( fet ) )
6559 result = QVariant::fromValue( fet );
6560
6561 return result;
6562}
6563
6564static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6565{
6566 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
6567 bool foundLayer = false;
6568 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6569
6570 //no layer found
6571 if ( !featureSource || !foundLayer )
6572 {
6573 return QVariant();
6574 }
6576 QString cacheValueKey;
6577 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
6578 {
6579 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6580
6581 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
6582 QString filterString;
6583 for ( ; i != attributeMap.constEnd(); ++i )
6584 {
6585 if ( !filterString.isEmpty() )
6586 {
6587 filterString.append( " AND " );
6588 }
6589 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
6590 }
6591 cacheValueKey = QStringLiteral( "getfeature:%1:%2" ).arg( featureSource->id(), filterString );
6592 if ( context && context->hasCachedValue( cacheValueKey ) )
6593 {
6594 return context->cachedValue( cacheValueKey );
6595 }
6596 req.setFilterExpression( filterString );
6597 }
6598 else
6599 {
6600 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6601 int attributeId = featureSource->fields().lookupField( attribute );
6602 if ( attributeId == -1 )
6603 {
6604 return QVariant();
6605 }
6606
6607 const QVariant &attVal = values.at( 2 );
6608
6609 cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
6610 if ( context && context->hasCachedValue( cacheValueKey ) )
6611 {
6612 return context->cachedValue( cacheValueKey );
6613 }
6614
6616 }
6617 req.setLimit( 1 );
6618 req.setTimeout( 10000 );
6619 req.setRequestMayBeNested( true );
6620 if ( context )
6621 req.setFeedback( context->feedback() );
6622 if ( !parent->needsGeometry() )
6623 {
6625 }
6626 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6627
6628 QgsFeature fet;
6629 QVariant res;
6630 if ( fIt.nextFeature( fet ) )
6631 {
6632 res = QVariant::fromValue( fet );
6633 }
6634
6635 if ( context )
6636 context->setCachedValue( cacheValueKey, res );
6637 return res;
6638}
6639
6640static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6641{
6642 QVariant result;
6643 QString fieldName;
6644
6645 if ( context )
6646 {
6647 if ( !values.isEmpty() )
6648 {
6649 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
6650 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
6651 fieldName = col->name();
6652 else if ( values.size() == 2 )
6653 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6654 }
6655
6656 QVariant value = values.at( 0 );
6657
6658 const QgsFields fields = context->fields();
6659 int fieldIndex = fields.lookupField( fieldName );
6660
6661 if ( fieldIndex == -1 )
6662 {
6663 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6664 }
6665 else
6666 {
6667 // TODO this function is NOT thread safe
6669 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
6671
6672 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
6673 if ( context->hasCachedValue( cacheValueKey ) )
6674 {
6675 return context->cachedValue( cacheValueKey );
6676 }
6677
6678 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
6680
6681 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
6682
6683 QVariant cache;
6684 if ( !context->hasCachedValue( cacheKey ) )
6685 {
6686 cache = formatter->createCache( layer, fieldIndex, setup.config() );
6687 context->setCachedValue( cacheKey, cache );
6688 }
6689 else
6690 cache = context->cachedValue( cacheKey );
6691
6692 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
6693
6694 context->setCachedValue( cacheValueKey, result );
6695 }
6696 }
6697 else
6698 {
6699 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6700 }
6701
6702 return result;
6703}
6704
6705static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6706{
6707 const QVariant data = values.at( 0 );
6708 const QMimeDatabase db;
6709 return db.mimeTypeForData( data.toByteArray() ).name();
6710}
6711
6712static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6713{
6714 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6715
6716 bool foundLayer = false;
6717 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [layerProperty]( QgsMapLayer * layer )-> QVariant
6718 {
6719 if ( !layer )
6720 return QVariant();
6721
6722 // here, we always prefer the layer metadata values over the older server-specific published values
6723 if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
6724 return layer->name();
6725 else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
6726 return layer->id();
6727 else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
6728 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
6729 else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
6730 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
6731 else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
6732 {
6733 QStringList keywords;
6734 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
6735 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
6736 {
6737 keywords.append( it.value() );
6738 }
6739 if ( !keywords.isEmpty() )
6740 return keywords;
6741 return layer->serverProperties()->keywordList();
6742 }
6743 else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
6744 return layer->serverProperties()->dataUrl();
6745 else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
6746 {
6747 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
6748 }
6749 else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
6750 return layer->serverProperties()->attributionUrl();
6751 else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
6752 return layer->publicSource();
6753 else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
6754 return layer->minimumScale();
6755 else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
6756 return layer->maximumScale();
6757 else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
6758 return layer->isEditable();
6759 else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
6760 return layer->crs().authid();
6761 else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
6762 return layer->crs().toProj();
6763 else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
6764 return layer->crs().description();
6765 else if ( QString::compare( layerProperty, QStringLiteral( "crs_ellipsoid" ), Qt::CaseInsensitive ) == 0 )
6766 return layer->crs().ellipsoidAcronym();
6767 else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
6768 {
6769 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
6770 QVariant result = QVariant::fromValue( extentGeom );
6771 return result;
6772 }
6773 else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
6774 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
6775 else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
6776 {
6777 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
6778 return decodedUri.value( QStringLiteral( "path" ) );
6779 }
6780 else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
6781 {
6782 switch ( layer->type() )
6783 {
6785 return QCoreApplication::translate( "expressions", "Vector" );
6787 return QCoreApplication::translate( "expressions", "Raster" );
6789 return QCoreApplication::translate( "expressions", "Mesh" );
6791 return QCoreApplication::translate( "expressions", "Vector Tile" );
6793 return QCoreApplication::translate( "expressions", "Plugin" );
6795 return QCoreApplication::translate( "expressions", "Annotation" );
6797 return QCoreApplication::translate( "expressions", "Point Cloud" );
6799 return QCoreApplication::translate( "expressions", "Group" );
6801 return QCoreApplication::translate( "expressions", "Tiled Scene" );
6802 }
6803 }
6804 else
6805 {
6806 //vector layer methods
6807 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
6808 if ( vLayer )
6809 {
6810 if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
6811 return vLayer->storageType();
6812 else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
6814 else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
6815 return QVariant::fromValue( vLayer->featureCount() );
6816 }
6817 }
6818
6819 return QVariant();
6820 }, foundLayer );
6821
6822 if ( !foundLayer )
6823 return QVariant();
6824 else
6825 return res;
6826}
6827
6828static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6829{
6830 const QString uriPart = values.at( 1 ).toString();
6831
6832 bool foundLayer = false;
6833
6834 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, uriPart]( QgsMapLayer * layer )-> QVariant
6835 {
6836 if ( !layer->dataProvider() )
6837 {
6838 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
6839 return QVariant();
6840 }
6841
6842 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
6843
6844 if ( !uriPart.isNull() )
6845 {
6846 return decodedUri.value( uriPart );
6847 }
6848 else
6849 {
6850 return decodedUri;
6851 }
6852 }, foundLayer );
6853
6854 if ( !foundLayer )
6855 {
6856 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
6857 return QVariant();
6858 }
6859 else
6860 {
6861 return res;
6862 }
6863}
6864
6865static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6866{
6867 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6868 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6869
6870 bool foundLayer = false;
6871 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, band, layerProperty]( QgsMapLayer * layer )-> QVariant
6872 {
6873 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
6874 if ( !rl )
6875 return QVariant();
6876
6877 if ( band < 1 || band > rl->bandCount() )
6878 {
6879 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
6880 return QVariant();
6881 }
6882
6884
6885 if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
6887 else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
6889 else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
6891 else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
6893 else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
6895 else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
6897 else
6898 {
6899 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
6900 return QVariant();
6901 }
6902
6903 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
6904 switch ( stat )
6905 {
6907 return stats.mean;
6909 return stats.stdDev;
6911 return stats.minimumValue;
6913 return stats.maximumValue;
6915 return stats.range;
6917 return stats.sum;
6918 default:
6919 break;
6920 }
6921 return QVariant();
6922 }, foundLayer );
6923
6924 if ( !foundLayer )
6925 {
6926#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
6927 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
6928#endif
6929 return QVariant();
6930 }
6931 else
6932 {
6933 return res;
6934 }
6935}
6936
6937static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6938{
6939 return values;
6940}
6941
6942static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6943{
6944 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6945 bool ascending = values.value( 1 ).toBool();
6946 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
6947 return list;
6948}
6949
6950static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6951{
6952 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
6953}
6954
6955static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6956{
6957 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
6958}
6959
6960static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6961{
6962 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
6963}
6964
6965static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6966{
6967 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6968 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6969 int match = 0;
6970 for ( const auto &item : listB )
6971 {
6972 if ( listA.contains( item ) )
6973 match++;
6974 }
6975
6976 return QVariant( match == listB.count() );
6977}
6978
6979static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6980{
6981 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
6982}
6983
6984static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6985{
6986 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6987 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6988 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
6989 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
6990 return list.at( list.length() + pos );
6991 return QVariant();
6992}
6993
6994static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6995{
6996 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6997 return list.value( 0 );
6998}
6999
7000static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7001{
7002 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7003 return list.value( list.size() - 1 );
7004}
7005
7006static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7007{
7008 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7009 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7010}
7011
7012static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7013{
7014 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7015 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7016}
7017
7018static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7019{
7020 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7021 int i = 0;
7022 double total = 0.0;
7023 for ( const QVariant &item : list )
7024 {
7025 switch ( item.userType() )
7026 {
7027 case QMetaType::Int:
7028 case QMetaType::UInt:
7029 case QMetaType::LongLong:
7030 case QMetaType::ULongLong:
7031 case QMetaType::Float:
7032 case QMetaType::Double:
7033 total += item.toDouble();
7034 ++i;
7035 break;
7036 }
7037 }
7038 return i == 0 ? QVariant() : total / i;
7039}
7040
7041static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7042{
7043 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7044 QVariantList numbers;
7045 for ( const auto &item : list )
7046 {
7047 switch ( item.userType() )
7048 {
7049 case QMetaType::Int:
7050 case QMetaType::UInt:
7051 case QMetaType::LongLong:
7052 case QMetaType::ULongLong:
7053 case QMetaType::Float:
7054 case QMetaType::Double:
7055 numbers.append( item );
7056 break;
7057 }
7058 }
7059 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7060 const int count = numbers.count();
7061 if ( count == 0 )
7062 {
7063 return QVariant();
7064 }
7065 else if ( count % 2 )
7066 {
7067 return numbers.at( count / 2 );
7068 }
7069 else
7070 {
7071 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7072 }
7073}
7074
7075static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7076{
7077 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7078 int i = 0;
7079 double total = 0.0;
7080 for ( const QVariant &item : list )
7081 {
7082 switch ( item.userType() )
7083 {
7084 case QMetaType::Int:
7085 case QMetaType::UInt:
7086 case QMetaType::LongLong:
7087 case QMetaType::ULongLong:
7088 case QMetaType::Float:
7089 case QMetaType::Double:
7090 total += item.toDouble();
7091 ++i;
7092 break;
7093 }
7094 }
7095 return i == 0 ? QVariant() : total;
7096}
7097
7098static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7099{
7100 QVariant result = value;
7101 result.convert( static_cast<int>( type ) );
7102 return result;
7103}
7104
7105static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7106{
7107 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7108 QHash< QVariant, int > hash;
7109 for ( const auto &item : list )
7110 {
7111 ++hash[item];
7112 }
7113 const QList< int > occurrences = hash.values();
7114 if ( occurrences.empty() )
7115 return QVariantList();
7116
7117 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7118
7119 const QString option = values.at( 1 ).toString();
7120 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7121 {
7122 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7123 }
7124 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7125 {
7126 if ( hash.isEmpty() )
7127 return QVariant();
7128
7129 return QVariant( hash.key( maxValue ) );
7130 }
7131 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7132 {
7133 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7134 }
7135 else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
7136 {
7137 if ( maxValue * 2 <= list.size() )
7138 return QVariant();
7139
7140 return QVariant( hash.key( maxValue ) );
7141 }
7142 else
7143 {
7144 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7145 return QVariant();
7146 }
7147}
7148
7149static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7150{
7151 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7152 QHash< QVariant, int > hash;
7153 for ( const auto &item : list )
7154 {
7155 ++hash[item];
7156 }
7157 const QList< int > occurrences = hash.values();
7158 if ( occurrences.empty() )
7159 return QVariantList();
7160
7161 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7162
7163 const QString option = values.at( 1 ).toString();
7164 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7165 {
7166 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7167 }
7168 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7169 {
7170 if ( hash.isEmpty() )
7171 return QVariant();
7172
7173 return QVariant( hash.key( minValue ) );
7174 }
7175 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7176 {
7177 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7178 }
7179 else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
7180 {
7181 if ( hash.isEmpty() )
7182 return QVariant();
7183
7184 // Remove the majority, all others are minority
7185 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7186 if ( maxValue * 2 > list.size() )
7187 hash.remove( hash.key( maxValue ) );
7188
7189 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7190 }
7191 else
7192 {
7193 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7194 return QVariant();
7195 }
7196}
7197
7198static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7199{
7200 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7201 list.append( values.at( 1 ) );
7202 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7203}
7204
7205static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7206{
7207 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7208 list.prepend( values.at( 1 ) );
7209 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7210}
7211
7212static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7213{
7214 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7215 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7216 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7217}
7218
7219static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7220{
7221 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7222 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7223 if ( position < 0 )
7224 position = position + list.length();
7225 if ( position >= 0 && position < list.length() )
7226 list.removeAt( position );
7227 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7228}
7229
7230static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7231{
7232 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7233 return QVariant();
7234
7235 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7236
7237 const QVariant toRemove = values.at( 1 );
7238 if ( QgsVariantUtils::isNull( toRemove ) )
7239 {
7240 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant & element )
7241 {
7242 return QgsVariantUtils::isNull( element );
7243 } ), list.end() );
7244 }
7245 else
7246 {
7247 list.removeAll( toRemove );
7248 }
7249 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7250}
7251
7252static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7253{
7254 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7255 {
7256 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7257
7258 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7259 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7260 {
7261 int index = list.indexOf( it.key() );
7262 while ( index >= 0 )
7263 {
7264 list.replace( index, it.value() );
7265 index = list.indexOf( it.key() );
7266 }
7267 }
7268
7269 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7270 }
7271 else if ( values.count() == 3 )
7272 {
7273 QVariantList before;
7274 QVariantList after;
7275 bool isSingleReplacement = false;
7276
7277 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7278 {
7279 before = QVariantList() << values.at( 1 );
7280 }
7281 else
7282 {
7283 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7284 }
7285
7286 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7287 {
7288 after = QVariantList() << values.at( 2 );
7289 isSingleReplacement = true;
7290 }
7291 else
7292 {
7293 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7294 }
7295
7296 if ( !isSingleReplacement && before.length() != after.length() )
7297 {
7298 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7299 return QVariant();
7300 }
7301
7302 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7303 for ( int i = 0; i < before.length(); i++ )
7304 {
7305 int index = list.indexOf( before.at( i ) );
7306 while ( index >= 0 )
7307 {
7308 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7309 index = list.indexOf( before.at( i ) );
7310 }
7311 }
7312
7313 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7314 }
7315 else
7316 {
7317 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7318 return QVariant();
7319 }
7320}
7321
7322static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7323{
7324 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7325 QVariantList list_new;
7326
7327 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7328 {
7329 while ( list.removeOne( cur ) )
7330 {
7331 list_new.append( cur );
7332 }
7333 }
7334
7335 list_new.append( list );
7336
7337 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7338}
7339
7340static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7341{
7342 QVariantList list;
7343 for ( const QVariant &cur : values )
7344 {
7345 list += QgsExpressionUtils::getListValue( cur, parent );
7346 }
7347 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7348}
7349
7350static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7351{
7352 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7353 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7354 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7355 int slice_length = 0;
7356 // negative positions means positions taken relative to the end of the array
7357 if ( start_pos < 0 )
7358 {
7359 start_pos = list.length() + start_pos;
7360 }
7361 if ( end_pos >= 0 )
7362 {
7363 slice_length = end_pos - start_pos + 1;
7364 }
7365 else
7366 {
7367 slice_length = list.length() + end_pos - start_pos + 1;
7368 }
7369 //avoid negative lengths in QList.mid function
7370 if ( slice_length < 0 )
7371 {
7372 slice_length = 0;
7373 }
7374 list = list.mid( start_pos, slice_length );
7375 return list;
7376}
7377
7378static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7379{
7380 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7381 std::reverse( list.begin(), list.end() );
7382 return list;
7383}
7384
7385static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7386{
7387 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7388 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7389 for ( const QVariant &cur : array2 )
7390 {
7391 if ( array1.contains( cur ) )
7392 return QVariant( true );
7393 }
7394 return QVariant( false );
7395}
7396
7397static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7398{
7399 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7400
7401 QVariantList distinct;
7402
7403 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7404 {
7405 if ( !distinct.contains( *it ) )
7406 {
7407 distinct += ( *it );
7408 }
7409 }
7410
7411 return distinct;
7412}
7413
7414static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7415{
7416 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7417 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7418 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7419
7420 QString str;
7421
7422 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7423 {
7424 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
7425 if ( it != ( array.constEnd() - 1 ) )
7426 {
7427 str += delimiter;
7428 }
7429 }
7430
7431 return QVariant( str );
7432}
7433
7434static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7435{
7436 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7437 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7438 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7439
7440 QStringList list = str.split( delimiter );
7441 QVariantList array;
7442
7443 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
7444 {
7445 array += ( !( *it ).isEmpty() ) ? *it : empty;
7446 }
7447
7448 return array;
7449}
7450
7451static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7452{
7453 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7454 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
7455 if ( document.isNull() )
7456 return QVariant();
7457
7458 return document.toVariant();
7459}
7460
7461static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7462{
7463 Q_UNUSED( parent )
7464 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
7465 return QString( document.toJson( QJsonDocument::Compact ) );
7466}
7467
7468static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7469{
7470 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7471 if ( str.isEmpty() )
7472 return QVariantMap();
7473 str = str.trimmed();
7474
7475 return QgsHstoreUtils::parse( str );
7476}
7477
7478static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7479{
7480 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7481 return QgsHstoreUtils::build( map );
7482}
7483
7484static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7485{
7486 QVariantMap result;
7487 for ( int i = 0; i + 1 < values.length(); i += 2 )
7488 {
7489 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
7490 }
7491 return result;
7492}
7493
7494static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7495{
7496 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7497 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7498 QVariantMap resultMap;
7499
7500 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7501 {
7502 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
7503 }
7504
7505 return resultMap;
7506}
7507
7508static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7509{
7510 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
7511}
7512
7513static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7514{
7515 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
7516}
7517
7518static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7519{
7520 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7521 map.remove( values.at( 1 ).toString() );
7522 return map;
7523}
7524
7525static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7526{
7527 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7528 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
7529 return map;
7530}
7531
7532static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7533{
7534 QVariantMap result;
7535 for ( const QVariant &cur : values )
7536 {
7537 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
7538 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
7539 result.insert( it.key(), it.value() );
7540 }
7541 return result;
7542}
7543
7544static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7545{
7546 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
7547}
7548
7549static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7550{
7551 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
7552}
7553
7554static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7555{
7556 const QString envVarName = values.at( 0 ).toString();
7557 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
7558 return QVariant();
7559
7560 return QProcessEnvironment::systemEnvironment().value( envVarName );
7561}
7562
7563static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7564{
7565 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7566 if ( parent->hasEvalError() )
7567 {
7568 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "base_file_name" ) ) );
7569 return QVariant();
7570 }
7571 return QFileInfo( file ).completeBaseName();
7572}
7573
7574static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7575{
7576 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7577 if ( parent->hasEvalError() )
7578 {
7579 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_suffix" ) ) );
7580 return QVariant();
7581 }
7582 return QFileInfo( file ).completeSuffix();
7583}
7584
7585static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7586{
7587 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7588 if ( parent->hasEvalError() )
7589 {
7590 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_exists" ) ) );
7591 return QVariant();
7592 }
7593 return QFileInfo::exists( file );
7594}
7595
7596static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7597{
7598 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7599 if ( parent->hasEvalError() )
7600 {
7601 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_name" ) ) );
7602 return QVariant();
7603 }
7604 return QFileInfo( file ).fileName();
7605}
7606
7607static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7608{
7609 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7610 if ( parent->hasEvalError() )
7611 {
7612 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_file" ) ) );
7613 return QVariant();
7614 }
7615 return QFileInfo( file ).isFile();
7616}
7617
7618static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7619{
7620 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7621 if ( parent->hasEvalError() )
7622 {
7623 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_directory" ) ) );
7624 return QVariant();
7625 }
7626 return QFileInfo( file ).isDir();
7627}
7628
7629static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7630{
7631 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7632 if ( parent->hasEvalError() )
7633 {
7634 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_path" ) ) );
7635 return QVariant();
7636 }
7637 return QDir::toNativeSeparators( QFileInfo( file ).path() );
7638}
7639
7640static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7641{
7642 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7643 if ( parent->hasEvalError() )
7644 {
7645 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_size" ) ) );
7646 return QVariant();
7647 }
7648 return QFileInfo( file ).size();
7649}
7650
7651static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
7652{
7653 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
7654}
7655
7656static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7657{
7658 QVariant hash;
7659 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7660 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
7661
7662 if ( method == QLatin1String( "md4" ) )
7663 {
7664 hash = fcnHash( str, QCryptographicHash::Md4 );
7665 }
7666 else if ( method == QLatin1String( "md5" ) )
7667 {
7668 hash = fcnHash( str, QCryptographicHash::Md5 );
7669 }
7670 else if ( method == QLatin1String( "sha1" ) )
7671 {
7672 hash = fcnHash( str, QCryptographicHash::Sha1 );
7673 }
7674 else if ( method == QLatin1String( "sha224" ) )
7675 {
7676 hash = fcnHash( str, QCryptographicHash::Sha224 );
7677 }
7678 else if ( method == QLatin1String( "sha256" ) )
7679 {
7680 hash = fcnHash( str, QCryptographicHash::Sha256 );
7681 }
7682 else if ( method == QLatin1String( "sha384" ) )
7683 {
7684 hash = fcnHash( str, QCryptographicHash::Sha384 );
7685 }
7686 else if ( method == QLatin1String( "sha512" ) )
7687 {
7688 hash = fcnHash( str, QCryptographicHash::Sha512 );
7689 }
7690 else if ( method == QLatin1String( "sha3_224" ) )
7691 {
7692 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
7693 }
7694 else if ( method == QLatin1String( "sha3_256" ) )
7695 {
7696 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
7697 }
7698 else if ( method == QLatin1String( "sha3_384" ) )
7699 {
7700 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
7701 }
7702 else if ( method == QLatin1String( "sha3_512" ) )
7703 {
7704 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
7705 }
7706 else if ( method == QLatin1String( "keccak_224" ) )
7707 {
7708 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
7709 }
7710 else if ( method == QLatin1String( "keccak_256" ) )
7711 {
7712 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
7713 }
7714 else if ( method == QLatin1String( "keccak_384" ) )
7715 {
7716 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
7717 }
7718 else if ( method == QLatin1String( "keccak_512" ) )
7719 {
7720 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
7721 }
7722 else
7723 {
7724 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
7725 }
7726 return hash;
7727}
7728
7729static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7730{
7731 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
7732}
7733
7734static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7735{
7736 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
7737}
7738
7739static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7740{
7741 const QByteArray input = values.at( 0 ).toByteArray();
7742 return QVariant( QString( input.toBase64() ) );
7743}
7744
7745static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7746{
7747 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7748 QUrlQuery query;
7749 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7750 {
7751 query.addQueryItem( it.key(), it.value().toString() );
7752 }
7753 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
7754}
7755
7756static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7757{
7758 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7759 const QByteArray base64 = value.toLocal8Bit();
7760 const QByteArray decoded = QByteArray::fromBase64( base64 );
7761 return QVariant( decoded );
7762}
7763
7764typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
7765
7766static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const RelationFunction &relationFunction, bool invert = false, double bboxGrow = 0, bool isNearestFunc = false, bool isIntersectsFunc = false )
7767{
7768
7769 if ( ! context )
7770 {
7771 parent->setEvalErrorString( QStringLiteral( "This function was called without an expression context." ) );
7772 return QVariant();
7773 }
7774
7775 const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
7776 // TODO this function is NOT thread safe
7778 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
7780
7781 QgsFeatureRequest request;
7782 request.setTimeout( 10000 );
7783 request.setRequestMayBeNested( true );
7784 request.setFeedback( context->feedback() );
7785
7786 // First parameter is the overlay layer
7787 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
7789
7790 const bool layerCanBeCached = node->isStatic( parent, context );
7791 QVariant targetLayerValue = node->eval( parent, context );
7793
7794 // Second parameter is the expression to evaluate (or null for testonly)
7795 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
7797 QString subExpString = node->dump();
7798
7799 bool testOnly = ( subExpString == "NULL" );
7800 // TODO this function is NOT thread safe
7802 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
7804 if ( !targetLayer ) // No layer, no joy
7805 {
7806 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
7807 return QVariant();
7808 }
7809
7810 // Third parameter is the filtering expression
7811 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
7813 QString filterString = node->dump();
7814 if ( filterString != "NULL" )
7815 {
7816 request.setFilterExpression( filterString ); //filter cached features
7817 }
7818
7819 // Fourth parameter is the limit
7820 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7822 QVariant limitValue = node->eval( parent, context );
7824 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
7825
7826 // Fifth parameter (for nearest only) is the max distance
7827 double max_distance = 0;
7828 if ( isNearestFunc ) //maxdistance param handling
7829 {
7830 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
7832 QVariant distanceValue = node->eval( parent, context );
7834 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
7835 }
7836
7837 // Fifth or sixth (for nearest only) parameter is the cache toggle
7838 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
7840 QVariant cacheValue = node->eval( parent, context );
7842 bool cacheEnabled = cacheValue.toBool();
7843
7844 // Sixth parameter (for intersects only) is the min overlap (area or length)
7845 // Seventh parameter (for intersects only) is the min inscribed circle radius
7846 // Eighth parameter (for intersects only) is the return_details
7847 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
7848 double minOverlap { -1 };
7849 double minInscribedCircleRadius { -1 };
7850 bool returnDetails = false; //#spellok
7851 bool sortByMeasure = false;
7852 bool sortAscending = false;
7853 bool requireMeasures = false;
7854 bool overlapOrRadiusFilter = false;
7855 if ( isIntersectsFunc )
7856 {
7857
7858 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7860 const QVariant minOverlapValue = node->eval( parent, context );
7862 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
7863 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7865 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
7867 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
7868 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
7869 // Return measures is only effective when an expression is set
7870 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
7871 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
7872 // Sort by measures is only effective when an expression is set
7873 const QString sorting { node->eval( parent, context ).toString().toLower() };
7874 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
7875 sortAscending = sorting.startsWith( "asc" );
7876 requireMeasures = sortByMeasure || returnDetails; //#spellok
7877 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
7878 }
7879
7880
7881 FEAT_FROM_CONTEXT( context, feat )
7882 const QgsGeometry geometry = feat.geometry();
7883
7884 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
7885 {
7886 QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
7887 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
7888 }
7889
7890 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
7891
7892 QgsRectangle intDomain = geometry.boundingBox();
7893 if ( bboxGrow != 0 )
7894 {
7895 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
7896 }
7897
7898 const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
7899
7900 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
7901 // Otherwise, it can be toggled by the user
7902 QgsSpatialIndex spatialIndex;
7903 QgsVectorLayer *cachedTarget;
7904 QList<QgsFeature> features;
7905 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
7906 {
7907 // If the cache (local spatial index) is enabled, we materialize the whole
7908 // layer, then do the request on that layer instead.
7909 const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
7910 const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
7911
7912 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
7913 {
7914 cachedTarget = targetLayer->materialize( request );
7915 if ( layerCanBeCached )
7916 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
7917 }
7918 else
7919 {
7920 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
7921 }
7922
7923 if ( !context->hasCachedValue( cacheIndex ) )
7924 {
7925 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
7926 if ( layerCanBeCached )
7927 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
7928 }
7929 else
7930 {
7931 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
7932 }
7933
7934 QList<QgsFeatureId> fidsList;
7935 if ( isNearestFunc )
7936 {
7937 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
7938 }
7939 else
7940 {
7941 fidsList = spatialIndex.intersects( intDomain );
7942 }
7943
7944 QListIterator<QgsFeatureId> i( fidsList );
7945 while ( i.hasNext() )
7946 {
7947 QgsFeatureId fId2 = i.next();
7948 if ( sameLayers && feat.id() == fId2 )
7949 continue;
7950 features.append( cachedTarget->getFeature( fId2 ) );
7951 }
7952
7953 }
7954 else
7955 {
7956 // If the cache (local spatial index) is not enabled, we directly
7957 // get the features from the target layer
7958 request.setFilterRect( intDomain );
7959 QgsFeatureIterator fit = targetLayer->getFeatures( request );
7960 QgsFeature feat2;
7961 while ( fit.nextFeature( feat2 ) )
7962 {
7963 if ( sameLayers && feat.id() == feat2.id() )
7964 continue;
7965 features.append( feat2 );
7966 }
7967 }
7968
7969 QgsExpression subExpression;
7970 QgsExpressionContext subContext;
7971 if ( !testOnly )
7972 {
7973 const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
7974 const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
7975
7976 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
7977 {
7978 subExpression = QgsExpression( subExpString );
7980 subExpression.prepare( &subContext );
7981 }
7982 else
7983 {
7984 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
7985 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
7986 }
7987 }
7988
7989 // //////////////////////////////////////////////////////////////////
7990 // Helper functions for geometry tests
7991
7992 // Test function for linestring geometries, returns TRUE if test passes
7993 auto testLinestring = [ = ]( const QgsGeometry intersection, double & overlapValue ) -> bool
7994 {
7995 bool testResult { false };
7996 // For return measures:
7997 QVector<double> overlapValues;
7998 const QgsGeometry merged { intersection.mergeLines() };
7999 for ( auto it = merged.const_parts_begin(); ! testResult && it != merged.const_parts_end(); ++it )
8000 {
8001 const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
8002 // Check min overlap for intersection (if set)
8003 if ( minOverlap != -1 || requireMeasures )
8004 {
8005 overlapValue = geom->length();
8006 overlapValues.append( overlapValue );
8007 if ( minOverlap != -1 )
8008 {
8009 if ( overlapValue >= minOverlap )
8010 {
8011 testResult = true;
8012 }
8013 else
8014 {
8015 continue;
8016 }
8017 }
8018 }
8019 }
8020
8021 if ( ! overlapValues.isEmpty() )
8022 {
8023 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8024 }
8025
8026 return testResult;
8027 };
8028
8029 // Test function for polygon geometries, returns TRUE if test passes
8030 auto testPolygon = [ = ]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
8031 {
8032 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8033 bool testResult { false };
8034 // For return measures:
8035 QVector<double> overlapValues;
8036 QVector<double> radiusValues;
8037 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8038 {
8039 const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
8040 // Check min overlap for intersection (if set)
8041 if ( minOverlap != -1 || requireMeasures )
8042 {
8043 overlapValue = geom->area();
8044 overlapValues.append( geom->area() );
8045 if ( minOverlap != - 1 )
8046 {
8047 if ( overlapValue >= minOverlap )
8048 {
8049 testResult = true;
8050 }
8051 else
8052 {
8053 continue;
8054 }
8055 }
8056 }
8057
8058 // Check min inscribed circle radius for intersection (if set)
8059 if ( minInscribedCircleRadius != -1 || requireMeasures )
8060 {
8061 const QgsRectangle bbox = geom->boundingBox();
8062 const double width = bbox.width();
8063 const double height = bbox.height();
8064 const double size = width > height ? width : height;
8065 const double tolerance = size / 100.0;
8066 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8067 testResult = radiusValue >= minInscribedCircleRadius;
8068 radiusValues.append( radiusValues );
8069 }
8070 } // end for parts
8071
8072 // Get the max values
8073 if ( !radiusValues.isEmpty() )
8074 {
8075 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8076 }
8077
8078 if ( ! overlapValues.isEmpty() )
8079 {
8080 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8081 }
8082
8083 return testResult;
8084
8085 };
8086
8087
8088 bool found = false;
8089 int foundCount = 0;
8090 QVariantList results;
8091
8092 QListIterator<QgsFeature> i( features );
8093 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8094 {
8095
8096 QgsFeature feat2 = i.next();
8097
8098
8099 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8100 {
8101
8102 double overlapValue = -1;
8103 double radiusValue = -1;
8104
8105 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8106 {
8107
8108 QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
8109
8110 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8111 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8112 {
8113 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8114 intersection = QgsGeometry();
8115 QgsMultiPolygonXY poly;
8116 QgsMultiPolylineXY line;
8117 QgsMultiPointXY point;
8118 for ( const auto &geom : std::as_const( geometries ) )
8119 {
8120 switch ( geom.type() )
8121 {
8123 {
8124 poly.append( geom.asPolygon() );
8125 break;
8126 }
8128 {
8129 line.append( geom.asPolyline() );
8130 break;
8131 }
8133 {
8134 point.append( geom.asPoint() );
8135 break;
8136 }
8139 {
8140 break;
8141 }
8142 }
8143 }
8144
8145 switch ( geometry.type() )
8146 {
8148 {
8149 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8150 break;
8151 }
8153 {
8154 intersection = QgsGeometry::fromMultiPolylineXY( line );
8155 break;
8156 }
8158 {
8159 intersection = QgsGeometry::fromMultiPointXY( point );
8160 break;
8161 }
8164 {
8165 break;
8166 }
8167 }
8168 }
8169
8170 // Depending on the intersection geometry type and on the geometry type of
8171 // the tested geometry we can run different tests and collect different measures
8172 // that can be used for sorting (if required).
8173 switch ( intersection.type() )
8174 {
8175
8177 {
8178
8179 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8180 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8181
8182 if ( ! testResult && overlapOrRadiusFilter )
8183 {
8184 continue;
8185 }
8186
8187 break;
8188 }
8189
8191 {
8192
8193 // If the intersection is a linestring and a minimum circle is required
8194 // we can discard this result immediately.
8195 if ( minInscribedCircleRadius != -1 )
8196 {
8197 continue;
8198 }
8199
8200 // Otherwise a test for the overlap value is performed.
8201 const bool testResult { testLinestring( intersection, overlapValue ) };
8202
8203 if ( ! testResult && overlapOrRadiusFilter )
8204 {
8205 continue;
8206 }
8207
8208 break;
8209 }
8210
8212 {
8213
8214 // If the intersection is a point and a minimum circle is required
8215 // we can discard this result immediately.
8216 if ( minInscribedCircleRadius != -1 )
8217 {
8218 continue;
8219 }
8220
8221 bool testResult { false };
8222 if ( minOverlap != -1 || requireMeasures )
8223 {
8224 // Initially set this to 0 because it's a point intersection...
8225 overlapValue = 0;
8226 // ... but if the target geometry is not a point and the source
8227 // geometry is a point, we must record the length or the area
8228 // of the intersected geometry and use that as a measure for
8229 // sorting or reporting.
8230 if ( geometry.type() == Qgis::GeometryType::Point )
8231 {
8232 switch ( feat2.geometry().type() )
8233 {
8237 {
8238 break;
8239 }
8241 {
8242 testResult = testLinestring( feat2.geometry(), overlapValue );
8243 break;
8244 }
8246 {
8247 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8248 break;
8249 }
8250 }
8251 }
8252
8253 if ( ! testResult && overlapOrRadiusFilter )
8254 {
8255 continue;
8256 }
8257
8258 }
8259 break;
8260 }
8261
8264 {
8265 continue;
8266 }
8267 }
8268 }
8269
8270 found = true;
8271 foundCount++;
8272
8273 // We just want a single boolean result if there is any intersect: finish and return true
8274 if ( testOnly )
8275 break;
8276
8277 if ( !invert )
8278 {
8279 // We want a list of attributes / geometries / other expression values, evaluate now
8280 subContext.setFeature( feat2 );
8281 const QVariant expResult = subExpression.evaluate( &subContext );
8282
8283 if ( requireMeasures )
8284 {
8285 QVariantMap resultRecord;
8286 resultRecord.insert( QStringLiteral( "id" ), feat2.id() );
8287 resultRecord.insert( QStringLiteral( "result" ), expResult );
8288 // Overlap is always added because return measures was set
8289 resultRecord.insert( QStringLiteral( "overlap" ), overlapValue );
8290 // Radius is only added when is different than -1 (because for linestrings is not set)
8291 if ( radiusValue != -1 )
8292 {
8293 resultRecord.insert( QStringLiteral( "radius" ), radiusValue );
8294 }
8295 results.append( resultRecord );
8296 }
8297 else
8298 {
8299 results.append( expResult );
8300 }
8301 }
8302 else
8303 {
8304 // If not, results is a list of found ids, which we'll inverse and evaluate below
8305 results.append( feat2.id() );
8306 }
8307 }
8308 }
8309
8310 if ( testOnly )
8311 {
8312 if ( invert )
8313 found = !found;//for disjoint condition
8314 return found;
8315 }
8316
8317 if ( !invert )
8318 {
8319 if ( requireMeasures )
8320 {
8321 if ( sortByMeasure )
8322 {
8323 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
8324 {
8325 return sortAscending ?
8326 recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble()
8327 : recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble();
8328 } );
8329 }
8330 // Resize
8331 if ( limit > 0 && results.size() > limit )
8332 {
8333 results.erase( results.begin() + limit );
8334 }
8335
8336 if ( ! returnDetails ) //#spellok
8337 {
8338 QVariantList expResults;
8339 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8340 {
8341 expResults.append( it->toMap().value( QStringLiteral( "result" ) ) );
8342 }
8343 return expResults;
8344 }
8345 }
8346
8347 return results;
8348 }
8349
8350 // for disjoint condition returns the results for cached layers not intersected feats
8351 QVariantList disjoint_results;
8352 QgsFeature feat2;
8353 QgsFeatureRequest request2;
8354 request2.setLimit( limit );
8355 if ( context )
8356 request2.setFeedback( context->feedback() );
8357 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
8358 while ( fi.nextFeature( feat2 ) )
8359 {
8360 if ( !results.contains( feat2.id() ) )
8361 {
8362 subContext.setFeature( feat2 );
8363 disjoint_results.append( subExpression.evaluate( &subContext ) );
8364 }
8365 }
8366 return disjoint_results;
8367
8368}
8369
8370// Intersect functions:
8371
8372static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8373{
8374 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
8375}
8376
8377static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8378{
8379 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
8380}
8381
8382static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8383{
8384 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
8385}
8386
8387static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8388{
8389 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
8390}
8391
8392static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8393{
8394 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
8395}
8396
8397static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8398{
8399 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
8400}
8402static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8403{
8404 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
8405}
8406
8407static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8408{
8409 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
8410}
8411
8412const QList<QgsExpressionFunction *> &QgsExpression::Functions()
8413{
8414 // The construction of the list isn't thread-safe, and without the mutex,
8415 // crashes in the WFS provider may occur, since it can parse expressions
8416 // in parallel.
8417 // The mutex needs to be recursive.
8418 QMutexLocker locker( &sFunctionsMutex );
8419
8420 QList<QgsExpressionFunction *> &functions = *sFunctions();
8421
8422 if ( functions.isEmpty() )
8423 {
8425 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
8426 << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
8427 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
8428
8429 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
8430 aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8431 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8432
8433 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
8434 aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8435
8436 functions
8437 << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
8438 << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
8439 << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
8440 << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
8441 << new QgsStaticExpressionFunction( QStringLiteral( "bearing" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "source_crs" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "ellipsoid" ), true, QVariant() ), fcnBearing, QStringLiteral( "GeometryGroup" ) )
8442 << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
8443 << new QgsStaticExpressionFunction( QStringLiteral( "project" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "elevation" ), true, M_PI_2 ), fcnProject, QStringLiteral( "GeometryGroup" ) )
8444 << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
8445 << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
8446 << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
8447 << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
8448 << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
8449 << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
8450 << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
8451 << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
8452 << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
8453 << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
8454 << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
8455 << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
8456 << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
8457
8458 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
8459 randFunc->setIsStatic( false );
8460 functions << randFunc;
8461
8462 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction( QStringLiteral( "randf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ), true, 0.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ), true, 1.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRndF, QStringLiteral( "Math" ) );
8463 randfFunc->setIsStatic( false );
8464 functions << randfFunc;
8465
8466 functions
8467 << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8468 << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8469 << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
8470 << new QgsStaticExpressionFunction( QStringLiteral( "scale_linear" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ), fcnLinearScale, QStringLiteral( "Math" ) )
8471 << new QgsStaticExpressionFunction( QStringLiteral( "scale_polynomial" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "exponent" ) ), fcnPolynomialScale, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "scale_exp" ) )
8472 << new QgsStaticExpressionFunction( QStringLiteral( "scale_exponential" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "exponent" ) ), fcnExponentialScale, QStringLiteral( "Math" ) )
8473 << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
8474 << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
8475 << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
8476 << new QgsStaticExpressionFunction( QStringLiteral( "to_bool" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToBool, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tobool" ), /* handlesNull = */ true )
8477 << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
8478 << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
8479 << new QgsStaticExpressionFunction( QStringLiteral( "to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToString, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tostring" ) )
8480 << new QgsStaticExpressionFunction( QStringLiteral( "to_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDateTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todatetime" ) )
8481 << new QgsStaticExpressionFunction( QStringLiteral( "to_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDate, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todate" ) )
8482 << new QgsStaticExpressionFunction( QStringLiteral( "to_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "totime" ) )
8483 << new QgsStaticExpressionFunction( QStringLiteral( "to_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInterval, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tointerval" ) )
8484 << new QgsStaticExpressionFunction( QStringLiteral( "to_dm" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinute, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todm" ) )
8485 << new QgsStaticExpressionFunction( QStringLiteral( "to_dms" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinuteSecond, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todms" ) )
8486 << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
8487 << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8488 << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
8489 << new QgsStaticExpressionFunction( QStringLiteral( "if" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "condition" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_true" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_false" ) ), fcnIf, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
8490 << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
8491
8492 << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
8494 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8495 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8496 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8497 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8498 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8499 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8500 fcnAggregate,
8501 QStringLiteral( "Aggregates" ),
8502 QString(),
8503 []( const QgsExpressionNodeFunction * node )
8504 {
8505 // usesGeometry callback: return true if @parent variable is referenced
8506
8507 if ( !node )
8508 return true;
8509
8510 if ( !node->args() )
8511 return false;
8512
8513 QSet<QString> referencedVars;
8514 if ( node->args()->count() > 2 )
8515 {
8516 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8517 referencedVars = subExpressionNode->referencedVariables();
8518 }
8519
8520 if ( node->args()->count() > 3 )
8521 {
8522 QgsExpressionNode *filterNode = node->args()->at( 3 );
8523 referencedVars.unite( filterNode->referencedVariables() );
8524 }
8525 return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
8526 },
8527 []( const QgsExpressionNodeFunction * node )
8528 {
8529 // referencedColumns callback: return AllAttributes if @parent variable is referenced
8530
8531 if ( !node )
8532 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8533
8534 if ( !node->args() )
8535 return QSet<QString>();
8536
8537 QSet<QString> referencedCols;
8538 QSet<QString> referencedVars;
8539
8540 if ( node->args()->count() > 2 )
8541 {
8542 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8543 referencedVars = subExpressionNode->referencedVariables();
8544 referencedCols = subExpressionNode->referencedColumns();
8545 }
8546 if ( node->args()->count() > 3 )
8547 {
8548 QgsExpressionNode *filterNode = node->args()->at( 3 );
8549 referencedVars = filterNode->referencedVariables();
8550 referencedCols.unite( filterNode->referencedColumns() );
8551 }
8552
8553 if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
8554 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8555 else
8556 return referencedCols;
8557 },
8558 true
8559 )
8560
8561 << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
8562 << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
8563 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8564 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8565 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8566 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8567 fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
8568
8569 << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8570 << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8571 << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8572 << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8573 << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8574 << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8575 << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8576 << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8577 << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8578 << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8579 << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8580 << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8581 << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8582 << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8583 << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8584 << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8585 << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8586 << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8587 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8588 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8589 << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8590
8591 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
8592 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
8593
8594 << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
8595 << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
8596 << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
8597 fcnAge, QStringLiteral( "Date and Time" ) )
8598 << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
8599 << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
8600 << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
8601 << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
8602 << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
8603 << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
8604 << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
8605 << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
8606 << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
8607 << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
8608 << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8609 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8610 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
8611 fcnMakeDate, QStringLiteral( "Date and Time" ) )
8612 << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8613 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8614 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8615 fcnMakeTime, QStringLiteral( "Date and Time" ) )
8616 << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8617 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8618 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
8619 << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8620 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8621 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8622 fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
8623 << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
8624 << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
8625 << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
8626 << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
8627 << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
8628 << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
8629 << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
8630 fcnMakeInterval, QStringLiteral( "Date and Time" ) )
8631 << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
8632 << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
8633 << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
8634 << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
8635 << new QgsStaticExpressionFunction( QStringLiteral( "ltrim" ), QgsExpressionFunction::ParameterList()
8636 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8637 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnLTrim, QStringLiteral( "String" ) )
8638 << new QgsStaticExpressionFunction( QStringLiteral( "rtrim" ), QgsExpressionFunction::ParameterList()
8639 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8640 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnRTrim, QStringLiteral( "String" ) )
8641 << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
8642 << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
8643 << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
8644 << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
8645 << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
8646 << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
8647 << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
8648 << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
8649 << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
8650 << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
8651 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
8652 << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
8653 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
8654 << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
8655 false, QSet< QString >(), false, QStringList(), true )
8656 << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8657 << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
8658 << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
8659 << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
8660 << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
8661 << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
8662 << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
8663 << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList()
8664 << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )
8665 << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 )
8666 << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() )
8667 << QgsExpressionFunction::Parameter( QStringLiteral( "omit_group_separators" ), true, false )
8668 << QgsExpressionFunction::Parameter( QStringLiteral( "trim_trailing_zeroes" ), true, false ), fcnFormatNumber, QStringLiteral( "String" ) )
8669 << new QgsStaticExpressionFunction( QStringLiteral( "format_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnFormatDate, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "Date and Time" ) )
8670 << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
8671 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8672 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8673 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8674 fcnColorMixRgb, QStringLiteral( "Color" ) )
8675 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8676 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8677 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8678 fcnColorMix, QStringLiteral( "Color" ) )
8679 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8680 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8681 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
8682 fcnColorRgb, QStringLiteral( "Color" ) )
8683 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgbf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8684 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8685 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8686 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8687 fcnColorRgbF, QStringLiteral( "Color" ) )
8688 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8689 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8690 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8691 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8692 fncColorRgba, QStringLiteral( "Color" ) )
8693 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8694 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8695 fcnRampColor, QStringLiteral( "Color" ) )
8696 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color_object" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8697 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8698 fcnRampColorObject, QStringLiteral( "Color" ) )
8699 << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
8700 << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
8701 fcnCreateRamp, QStringLiteral( "Color" ) )
8702 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8703 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8704 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
8705 fcnColorHsl, QStringLiteral( "Color" ) )
8706 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8707 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8708 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8709 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8710 fncColorHsla, QStringLiteral( "Color" ) )
8711 << new QgsStaticExpressionFunction( QStringLiteral( "color_hslf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8712 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8713 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8714 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8715 fcnColorHslF, QStringLiteral( "Color" ) )
8716 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8717 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8718 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8719 fcnColorHsv, QStringLiteral( "Color" ) )
8720 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8721 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8722 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8723 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8724 fncColorHsva, QStringLiteral( "Color" ) )
8725 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsvf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8726 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8727 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8728 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8729 fcnColorHsvF, QStringLiteral( "Color" ) )
8730 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8731 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8732 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8733 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
8734 fcnColorCmyk, QStringLiteral( "Color" ) )
8735 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8736 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8737 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8738 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8739 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8740 fncColorCmyka, QStringLiteral( "Color" ) )
8741 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmykf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8742 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8743 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8744 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8745 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8746 fcnColorCmykF, QStringLiteral( "Color" ) )
8747 << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8748 << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
8749 fncColorPart, QStringLiteral( "Color" ) )
8750 << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8751 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8752 fncDarker, QStringLiteral( "Color" ) )
8753 << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8754 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8755 fncLighter, QStringLiteral( "Color" ) )
8756 << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
8757
8758 // file info
8759 << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8760 fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
8761 << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8762 fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
8763 << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8764 fcnFileExists, QStringLiteral( "Files and Paths" ) )
8765 << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8766 fcnFileName, QStringLiteral( "Files and Paths" ) )
8767 << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8768 fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
8769 << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8770 fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
8771 << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8772 fcnFilePath, QStringLiteral( "Files and Paths" ) )
8773 << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8774 fcnFileSize, QStringLiteral( "Files and Paths" ) )
8775
8776 << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
8777 fcnExif, QStringLiteral( "Files and Paths" ) )
8778 << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8779 fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
8780
8781 // hash
8782 << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
8783 fcnGenericHash, QStringLiteral( "Conversions" ) )
8784 << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8785 fcnHashMd5, QStringLiteral( "Conversions" ) )
8786 << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8787 fcnHashSha256, QStringLiteral( "Conversions" ) )
8788
8789 //base64
8790 << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8791 fcnToBase64, QStringLiteral( "Conversions" ) )
8792 << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8793 fcnFromBase64, QStringLiteral( "Conversions" ) )
8794
8795 // deprecated stuff - hidden from users
8796 << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
8797
8798 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
8799 geomFunc->setIsStatic( false );
8800 functions << geomFunc;
8801
8802 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
8803 areaFunc->setIsStatic( false );
8804 functions << areaFunc;
8805
8806 functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
8807
8808 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
8809 lengthFunc->setIsStatic( false );
8810 functions << lengthFunc;
8811
8812 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
8813 perimeterFunc->setIsStatic( false );
8814 functions << perimeterFunc;
8815
8816 functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
8817
8818 functions << new QgsStaticExpressionFunction( QStringLiteral( "roundness" ),
8820 fcnRoundness, QStringLiteral( "GeometryGroup" ) );
8821
8822 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
8823 xFunc->setIsStatic( false );
8824 functions << xFunc;
8825
8826 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
8827 yFunc->setIsStatic( false );
8828 functions << yFunc;
8829
8830 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
8831 zFunc->setIsStatic( false );
8832 functions << zFunc;
8833
8834 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
8835 {
8836 { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
8837 { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
8838 { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
8839 { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
8840 { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
8841 { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
8842 { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
8843 };
8844 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
8845 while ( i.hasNext() )
8846 {
8847 i.next();
8849 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8850 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8851 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8852 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
8853 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false )
8854 << QgsExpressionFunction::Parameter( QStringLiteral( "min_overlap" ), true, QVariant( -1 ), false )
8855 << QgsExpressionFunction::Parameter( QStringLiteral( "min_inscribed_circle_radius" ), true, QVariant( -1 ), false )
8856 << QgsExpressionFunction::Parameter( QStringLiteral( "return_details" ), true, false, false )
8857 << QgsExpressionFunction::Parameter( QStringLiteral( "sort_by_intersection_size" ), true, QString(), false ),
8858 i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8859
8860 // The current feature is accessed for the geometry, so this should not be cached
8861 fcnGeomOverlayFunc->setIsStatic( false );
8862 functions << fcnGeomOverlayFunc;
8863 }
8864
8865 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
8866 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8867 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8868 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8869 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
8870 << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
8871 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
8872 fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8873 // The current feature is accessed for the geometry, so this should not be cached
8874 fcnGeomOverlayNearestFunc->setIsStatic( false );
8875 functions << fcnGeomOverlayNearestFunc;
8876
8877 functions
8878 << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
8879 << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
8880 << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
8881 << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
8882 << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
8883 << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
8884 << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
8885 << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
8886 << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8887 << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
8888 fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
8889 << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
8890 << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
8891 << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
8892 << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
8893 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
8894 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
8895 fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
8896 << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
8897 << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
8898 << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8899 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8900 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
8901 fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
8902 << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
8903 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8904 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8905 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8906 fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
8907 << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
8908 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8909 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
8910 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
8911 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
8912 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8913 fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
8914 << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
8915 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8916 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8917 << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
8918 << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
8919 fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
8920 << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
8921 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8922 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
8923 fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
8924 << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
8925 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8926 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8927 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
8928 << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
8929 fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) )
8930 << new QgsStaticExpressionFunction( QStringLiteral( "make_valid" ), QgsExpressionFunction::ParameterList
8931 {
8932 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8933#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
8934 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "linework" ) ),
8935#else
8936 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "structure" ) ),
8937#endif
8938 QgsExpressionFunction::Parameter( QStringLiteral( "keep_collapsed" ), true, false )
8939 }, fcnGeomMakeValid, QStringLiteral( "GeometryGroup" ) );
8940
8941 functions << new QgsStaticExpressionFunction( QStringLiteral( "x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnXat, QStringLiteral( "GeometryGroup" ) );
8942 functions << new QgsStaticExpressionFunction( QStringLiteral( "y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnYat, QStringLiteral( "GeometryGroup" ) );
8943 functions << new QgsStaticExpressionFunction( QStringLiteral( "z_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnZat, QStringLiteral( "GeometryGroup" ) );
8944 functions << new QgsStaticExpressionFunction( QStringLiteral( "m_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnMat, QStringLiteral( "GeometryGroup" ) );
8945
8946 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) );
8947 xAtFunc->setIsStatic( false );
8948 functions << xAtFunc;
8949
8950
8951 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) );
8952 yAtFunc->setIsStatic( false );
8953 functions << yAtFunc;
8954
8955 functions
8956 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
8957 << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
8958 << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
8959 << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
8960 << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
8961 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
8962 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
8963 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
8964 << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
8965 << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
8966 << new QgsStaticExpressionFunction( QStringLiteral( "intersects_bbox" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ), fcnBbox, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "bbox" ) )
8967 << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8968 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8969 fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
8970 << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8971 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8972 fcnIntersects, QStringLiteral( "GeometryGroup" ) )
8973 << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8974 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8975 fcnTouches, QStringLiteral( "GeometryGroup" ) )
8976 << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8977 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8978 fcnCrosses, QStringLiteral( "GeometryGroup" ) )
8979 << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8980 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8981 fcnContains, QStringLiteral( "GeometryGroup" ) )
8982 << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8983 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8984 fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
8985 << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8986 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8987 fcnWithin, QStringLiteral( "GeometryGroup" ) )
8988 << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8989 << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
8990 << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
8991 fcnTranslate, QStringLiteral( "GeometryGroup" ) )
8992 << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8993 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
8994 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true )
8995 << QgsExpressionFunction::Parameter( QStringLiteral( "per_part" ), true, false ),
8996 fcnRotate, QStringLiteral( "GeometryGroup" ) )
8997 << new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8998 << QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
8999 << QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
9000 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
9001 fcnScale, QStringLiteral( "GeometryGroup" ) )
9002 << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9003 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
9004 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
9005 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
9006 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
9007 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
9008 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
9009 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
9010 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
9011 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
9012 fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
9013 << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9014 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9015 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
9016 << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
9017 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
9018 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
9019 fcnBuffer, QStringLiteral( "GeometryGroup" ) )
9020 << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9021 fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
9022 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_cw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9023 fcnForcePolygonCW, QStringLiteral( "GeometryGroup" ) )
9024 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_ccw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9025 fcnForcePolygonCCW, QStringLiteral( "GeometryGroup" ) )
9026 << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
9027 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
9028 << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
9029 << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
9030 << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
9031 << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9032 << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
9033 << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
9034 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9035 , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
9036 << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9037 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9038 , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
9039 << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9040 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9041 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9042 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
9043 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
9044 fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
9045 << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9046 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9047 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9048 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
9049 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
9050 fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
9051 << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9052 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
9053 << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
9054 fcnExtend, QStringLiteral( "GeometryGroup" ) )
9055 << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
9056 << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
9057 << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9058 << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
9059 << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, QStringLiteral( "GeometryGroup" ) )
9060 << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
9061 << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9062 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
9063 fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
9064 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9065 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
9066 fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
9067 << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
9068 << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
9069 << new QgsStaticExpressionFunction( QStringLiteral( "shared_paths" ), QgsExpressionFunction::ParameterList
9070 {
9071 QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ),
9072 QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9073 }, fcnSharedPaths, QStringLiteral( "GeometryGroup" ) )
9074 << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
9075 << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
9076 << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
9077 << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
9078 << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
9079 << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
9080 << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
9081 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave" ),
9082 {
9083 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9084 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9085 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9086 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9087 }, fcnTriangularWave, QStringLiteral( "GeometryGroup" ) )
9088 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave_randomized" ),
9089 {
9090 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9091 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9092 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9093 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9094 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9095 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9096 }, fcnTriangularWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9097 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave" ),
9098 {
9099 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9100 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9101 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9102 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9103 }, fcnSquareWave, QStringLiteral( "GeometryGroup" ) )
9104 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave_randomized" ),
9105 {
9106 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9107 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9108 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9109 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9110 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9111 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9112 }, fcnSquareWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9113 << new QgsStaticExpressionFunction( QStringLiteral( "wave" ),
9114 {
9115 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9116 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9117 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9118 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9119 }, fcnRoundWave, QStringLiteral( "GeometryGroup" ) )
9120 << new QgsStaticExpressionFunction( QStringLiteral( "wave_randomized" ),
9121 {
9122 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9123 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9124 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9125 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9126 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9127 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9128 }, fcnRoundWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9129 << new QgsStaticExpressionFunction( QStringLiteral( "apply_dash_pattern" ),
9130 {
9131 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9132 QgsExpressionFunction::Parameter( QStringLiteral( "pattern" ) ),
9133 QgsExpressionFunction::Parameter( QStringLiteral( "start_rule" ), true, QStringLiteral( "no_rule" ) ),
9134 QgsExpressionFunction::Parameter( QStringLiteral( "end_rule" ), true, QStringLiteral( "no_rule" ) ),
9135 QgsExpressionFunction::Parameter( QStringLiteral( "adjustment" ), true, QStringLiteral( "both" ) ),
9136 QgsExpressionFunction::Parameter( QStringLiteral( "pattern_offset" ), true, 0 ),
9137 }, fcnApplyDashPattern, QStringLiteral( "GeometryGroup" ) )
9138 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_count" ),
9139 {
9140 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9141 QgsExpressionFunction::Parameter( QStringLiteral( "vertices" ) )
9142 }, fcnDensifyByCount, QStringLiteral( "GeometryGroup" ) )
9143 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_distance" ),
9144 {
9145 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9146 QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9147 }, fcnDensifyByDistance, QStringLiteral( "GeometryGroup" ) )
9148 << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
9149 << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
9150 << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
9151 << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
9152 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
9153 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
9154 << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
9155 << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
9156 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
9157 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9158 << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
9159#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
9160 << new QgsStaticExpressionFunction( QStringLiteral( "concave_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9161 << QgsExpressionFunction::Parameter( QStringLiteral( "target_percent" ) )
9162 << QgsExpressionFunction::Parameter( QStringLiteral( "allow_holes" ), true, false ), fcnConcaveHull, QStringLiteral( "GeometryGroup" ) )
9163#endif
9164 << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
9165 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9166 fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
9167 << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
9168 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9169 fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
9170 << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
9171 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9172 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
9173 fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
9174 << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9175 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9176 fcnDifference, QStringLiteral( "GeometryGroup" ) )
9177 << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9178 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9179 fcnDistance, QStringLiteral( "GeometryGroup" ) )
9180 << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9181 << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
9182 fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
9183 << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9184 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9185 fcnIntersection, QStringLiteral( "GeometryGroup" ) )
9186 << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9187 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9188 fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
9189 << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9190 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9191 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9192 << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9193 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9194 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9195 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9196 << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
9197 fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
9198 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9199 fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
9200 << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
9201 << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9202 << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
9203 << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
9204 fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
9205 << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9206 << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
9207 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
9208 fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
9209 << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9210 fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
9211 << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9212 fcnZMax, QStringLiteral( "GeometryGroup" ) )
9213 << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9214 fcnZMin, QStringLiteral( "GeometryGroup" ) )
9215 << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9216 fcnMMax, QStringLiteral( "GeometryGroup" ) )
9217 << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9218 fcnMMin, QStringLiteral( "GeometryGroup" ) )
9219 << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9220 fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
9221 << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9222 fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
9223
9224
9225 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9226 << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
9227 << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
9228 fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
9229
9230 orderPartsFunc->setIsStaticFunction(
9231 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9232 {
9233 const QList< QgsExpressionNode *> argList = node->args()->list();
9234 for ( QgsExpressionNode *argNode : argList )
9235 {
9236 if ( !argNode->isStatic( parent, context ) )
9237 return false;
9238 }
9239
9240 if ( node->args()->count() > 1 )
9241 {
9242 QgsExpressionNode *argNode = node->args()->at( 1 );
9243
9244 QString expString = argNode->eval( parent, context ).toString();
9245
9246 QgsExpression e( expString );
9247
9248 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9249 return true;
9250 }
9251
9252 return true;
9253 } );
9254
9255 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9256 {
9257 if ( node->args()->count() > 1 )
9258 {
9259 QgsExpressionNode *argNode = node->args()->at( 1 );
9260 QString expression = argNode->eval( parent, context ).toString();
9262 e.prepare( context );
9263 context->setCachedValue( expression, QVariant::fromValue( e ) );
9264 }
9265 return true;
9266 }
9267 );
9268 functions << orderPartsFunc;
9269
9270 functions
9271 << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9272 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9273 fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
9274 << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9275 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9276 fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
9277 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9278 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
9279 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9280 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9281 fcnLineInterpolatePointByM, QStringLiteral( "GeometryGroup" ) )
9282 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9283 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
9284 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9285 << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
9286 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9287 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9288 fcnLineLocateM, QStringLiteral( "GeometryGroup" ) )
9289 << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9290 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
9291 << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9292 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
9293 << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9294 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
9295
9296
9297 // **Record** functions
9298
9299 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
9300 idFunc->setIsStatic( false );
9301 functions << idFunc;
9302
9303 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
9304 currentFeatureFunc->setIsStatic( false );
9305 functions << currentFeatureFunc;
9306
9307 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction( QStringLiteral( "uuid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QStringLiteral( "WithBraces" ) ), fcnUuid, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$uuid" ) );
9308 uuidFunc->setIsStatic( false );
9309 functions << uuidFunc;
9310
9311 functions
9312 << new QgsStaticExpressionFunction( QStringLiteral( "feature_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetFeatureId, QStringLiteral( "Record and Attributes" ), QString(), true )
9313 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9314 << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
9315 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ), true ),
9316 fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
9317 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9318 << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
9319 fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
9320
9321 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
9322 fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9323 attributesFunc->setIsStatic( false );
9324 functions << attributesFunc;
9325 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
9326 fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9327 representAttributesFunc->setIsStatic( false );
9328 functions << representAttributesFunc;
9329
9330 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction( QStringLiteral( "is_feature_valid" ),
9331 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9332 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9333 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9334 fcnValidateFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9335 validateFeature->setIsStatic( false );
9336 functions << validateFeature;
9337
9338 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction( QStringLiteral( "is_attribute_valid" ),
9339 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ), false )
9340 << QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9341 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9342 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9343 fcnValidateAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9344 validateAttribute->setIsStatic( false );
9345 functions << validateAttribute;
9346
9348 QStringLiteral( "maptip" ),
9349 -1,
9350 fcnFeatureMaptip,
9351 QStringLiteral( "Record and Attributes" ),
9352 QString(),
9353 false,
9354 QSet<QString>()
9355 );
9356 maptipFunc->setIsStatic( false );
9357 functions << maptipFunc;
9358
9360 QStringLiteral( "display_expression" ),
9361 -1,
9362 fcnFeatureDisplayExpression,
9363 QStringLiteral( "Record and Attributes" ),
9364 QString(),
9365 false,
9366 QSet<QString>()
9367 );
9368 displayFunc->setIsStatic( false );
9369 functions << displayFunc;
9370
9372 QStringLiteral( "is_selected" ),
9373 -1,
9374 fcnIsSelected,
9375 QStringLiteral( "Record and Attributes" ),
9376 QString(),
9377 false,
9378 QSet<QString>()
9379 );
9380 isSelectedFunc->setIsStatic( false );
9381 functions << isSelectedFunc;
9382
9383 functions
9385 QStringLiteral( "num_selected" ),
9386 -1,
9387 fcnNumSelected,
9388 QStringLiteral( "Record and Attributes" ),
9389 QString(),
9390 false,
9391 QSet<QString>()
9392 );
9393
9394 functions
9396 QStringLiteral( "sqlite_fetch_and_increment" ),
9398 << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
9399 << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
9400 << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
9401 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
9402 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
9403 << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
9404 fcnSqliteFetchAndIncrement,
9405 QStringLiteral( "Record and Attributes" )
9406 );
9407
9408 // **CRS** functions
9409 functions
9410 << new QgsStaticExpressionFunction( QStringLiteral( "crs_to_authid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "crs" ) ), fcnCrsToAuthid, QStringLiteral( "CRS" ), QString(), true )
9411 << new QgsStaticExpressionFunction( QStringLiteral( "crs_from_text" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "definition" ) ), fcnCrsFromText, QStringLiteral( "CRS" ) );
9412
9413
9414 // **Fields and Values** functions
9415 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
9416
9417 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9418 {
9419 Q_UNUSED( context )
9420 if ( node->args()->count() == 1 )
9421 {
9422 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
9423 if ( colRef )
9424 {
9425 return true;
9426 }
9427 else
9428 {
9429 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
9430 return false;
9431 }
9432 }
9433 else if ( node->args()->count() == 2 )
9434 {
9435 return true;
9436 }
9437 else
9438 {
9439 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
9440 return false;
9441 }
9442 }
9443 );
9444
9445 functions << representValueFunc;
9446
9447 // **General** functions
9448 functions
9449 << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9450 << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
9451 fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
9452 << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
9454 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9455 << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
9456 fcnDecodeUri, QStringLiteral( "Map Layers" ) )
9457 << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
9459 << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
9460 fcnMimeType, QStringLiteral( "General" ) )
9461 << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9462 << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
9463 << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
9464
9465 // **var** function
9466 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
9467 varFunction->setIsStaticFunction(
9468 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9469 {
9470 /* A variable node is static if it has a static name and the name can be found at prepare
9471 * time and is tagged with isStatic.
9472 * It is not static if a variable is set during iteration or not tagged isStatic.
9473 * (e.g. geom_part variable)
9474 */
9475 if ( node->args()->count() > 0 )
9476 {
9477 QgsExpressionNode *argNode = node->args()->at( 0 );
9478
9479 if ( !argNode->isStatic( parent, context ) )
9480 return false;
9481
9482 const QString varName = argNode->eval( parent, context ).toString();
9483 if ( varName == QLatin1String( "feature" ) || varName == QLatin1String( "id" ) || varName == QLatin1String( "geometry" ) )
9484 return false;
9485
9486 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
9487 return scope ? scope->isStatic( varName ) : false;
9488 }
9489 return false;
9490 }
9491 );
9492 varFunction->setUsesGeometryFunction(
9493 []( const QgsExpressionNodeFunction * node ) -> bool
9494 {
9495 if ( node && node->args()->count() > 0 )
9496 {
9497 QgsExpressionNode *argNode = node->args()->at( 0 );
9498 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
9499 {
9500 if ( literal->value() == QLatin1String( "geometry" ) || literal->value() == QLatin1String( "feature" ) )
9501 return true;
9502 }
9503 }
9504 return false;
9505 }
9506 );
9507
9508 functions
9509 << varFunction;
9510
9511 QgsStaticExpressionFunction *evalTemplateFunction = new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9512 evalTemplateFunction->setIsStaticFunction(
9513 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9514 {
9515 if ( node->args()->count() > 0 )
9516 {
9517 QgsExpressionNode *argNode = node->args()->at( 0 );
9518
9519 if ( argNode->isStatic( parent, context ) )
9520 {
9521 QString expString = argNode->eval( parent, context ).toString();
9522
9523 QgsExpression e( expString );
9524
9525 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9526 return true;
9527 }
9528 }
9529
9530 return false;
9531 } );
9532 functions << evalTemplateFunction;
9533
9534 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9535 evalFunc->setIsStaticFunction(
9536 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9537 {
9538 if ( node->args()->count() > 0 )
9539 {
9540 QgsExpressionNode *argNode = node->args()->at( 0 );
9541
9542 if ( argNode->isStatic( parent, context ) )
9543 {
9544 QString expString = argNode->eval( parent, context ).toString();
9545
9546 QgsExpression e( expString );
9547
9548 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9549 return true;
9550 }
9551 }
9552
9553 return false;
9554 } );
9555
9556 functions << evalFunc;
9557
9558 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9559 attributeFunc->setIsStaticFunction(
9560 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9561 {
9562 const QList< QgsExpressionNode *> argList = node->args()->list();
9563 for ( QgsExpressionNode *argNode : argList )
9564 {
9565 if ( !argNode->isStatic( parent, context ) )
9566 return false;
9567 }
9568
9569 if ( node->args()->count() == 1 )
9570 {
9571 // not static -- this is the variant which uses the current feature taken direct from the expression context
9572 return false;
9573 }
9574
9575 return true;
9576 } );
9577 functions << attributeFunc;
9578
9579 functions
9580 << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
9582 << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
9583 << new QgsStaticExpressionFunction( QStringLiteral( "raster_attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterAttributes, QStringLiteral( "Rasters" ) )
9584
9585 // functions for arrays
9588 << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9589 << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
9590 << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
9591 << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
9592 << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
9593 << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
9594 << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
9595 << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
9596 << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
9597 << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
9598 << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
9599 << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
9600 << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
9601 << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
9602 << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
9603 << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
9604 << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
9605 << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
9606 << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
9607 << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
9608 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
9609 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9610 << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
9611 << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
9612 << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
9613 << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
9614 << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
9615 << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
9616 << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
9617 << new QgsStaticExpressionFunction( QStringLiteral( "array_to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnArrayToString, QStringLiteral( "Arrays" ) )
9618 << new QgsStaticExpressionFunction( QStringLiteral( "string_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnStringToArray, QStringLiteral( "Arrays" ) )
9619 << new QgsStaticExpressionFunction( QStringLiteral( "generate_series" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "stop" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "step" ), true, 1.0 ), fcnGenerateSeries, QStringLiteral( "Arrays" ) )
9620 << new QgsStaticExpressionFunction( QStringLiteral( "geometries_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometries" ) ), fcnGeometryCollectionAsArray, QStringLiteral( "Arrays" ) )
9621
9622 //functions for maps
9623 << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
9624 << new QgsStaticExpressionFunction( QStringLiteral( "to_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "json_string" ) ), fcnWriteJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "map_to_json" ) )
9625 << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
9626 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
9627 << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
9628 << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
9629 << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
9630 << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
9631 << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
9632 << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
9633 << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
9634 << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
9635 << new QgsStaticExpressionFunction( QStringLiteral( "map_prefix_keys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
9636 << QgsExpressionFunction::Parameter( QStringLiteral( "prefix" ) ),
9637 fcnMapPrefixKeys, QStringLiteral( "Maps" ) )
9638 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_table" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9639 fcnMapToHtmlTable, QStringLiteral( "Maps" ) )
9640 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_dl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9641 fcnMapToHtmlDefinitionList, QStringLiteral( "Maps" ) )
9642 << new QgsStaticExpressionFunction( QStringLiteral( "url_encode" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9643 fcnToFormUrlEncode, QStringLiteral( "Maps" ) )
9644
9645 ;
9646
9648
9649 //QgsExpression has ownership of all built-in functions
9650 for ( QgsExpressionFunction *func : std::as_const( functions ) )
9651 {
9652 *sOwnedFunctions() << func;
9653 *sBuiltinFunctions() << func->name();
9654 sBuiltinFunctions()->append( func->aliases() );
9655 }
9656 }
9657 return functions;
9658}
9659
9660bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
9661{
9662 int fnIdx = functionIndex( function->name() );
9663 if ( fnIdx != -1 )
9664 {
9665 return false;
9666 }
9667
9668 QMutexLocker locker( &sFunctionsMutex );
9669 sFunctions()->append( function );
9670 if ( transferOwnership )
9671 sOwnedFunctions()->append( function );
9672
9673 return true;
9674}
9675
9676bool QgsExpression::unregisterFunction( const QString &name )
9677{
9678 // You can never override the built in functions.
9679 if ( QgsExpression::BuiltinFunctions().contains( name ) )
9680 {
9681 return false;
9682 }
9683 int fnIdx = functionIndex( name );
9684 if ( fnIdx != -1 )
9685 {
9686 QMutexLocker locker( &sFunctionsMutex );
9687 sFunctions()->removeAt( fnIdx );
9688 sFunctionIndexMap.clear();
9689 return true;
9690 }
9691 return false;
9692}
9693
9695{
9696 qDeleteAll( *sOwnedFunctions() );
9697 sOwnedFunctions()->clear();
9699
9700const QStringList &QgsExpression::BuiltinFunctions()
9701{
9702 if ( sBuiltinFunctions()->isEmpty() )
9703 {
9704 Functions(); // this method builds the gmBuiltinFunctions as well
9705 }
9706 return *sBuiltinFunctions();
9708
9710 : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
9711 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9712 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9713 QStringLiteral( "Arrays" ) )
9714{
9715
9716}
9717
9719{
9720 bool isStatic = false;
9721
9722 QgsExpressionNode::NodeList *args = node->args();
9724 if ( args->count() < 2 )
9725 return false;
9726
9727 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9728 {
9729 isStatic = true;
9730 }
9731 return isStatic;
9732}
9733
9735{
9736 Q_UNUSED( node )
9737 QVariantList result;
9738
9739 if ( args->count() < 2 )
9740 // error
9741 return result;
9742
9743 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9744
9745 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9746 std::unique_ptr< QgsExpressionContext > tempContext;
9747 if ( !subContext )
9748 {
9749 tempContext = std::make_unique< QgsExpressionContext >();
9750 subContext = tempContext.get();
9751 }
9752
9754 subContext->appendScope( subScope );
9755
9756 int i = 0;
9757 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
9758 {
9759 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
9760 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), i, true ) );
9761 result << args->at( 1 )->eval( parent, subContext );
9762 }
9763
9764 if ( context )
9765 delete subContext->popScope();
9766
9767 return result;
9768}
9769
9770QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9772 // This is a dummy function, all the real handling is in run
9773 Q_UNUSED( values )
9774 Q_UNUSED( context )
9775 Q_UNUSED( parent )
9776 Q_UNUSED( node )
9777
9778 Q_ASSERT( false );
9779 return QVariant();
9780}
9781
9783{
9784 QgsExpressionNode::NodeList *args = node->args();
9785
9786 if ( args->count() < 2 )
9787 // error
9788 return false;
9789
9790 args->at( 0 )->prepare( parent, context );
9791
9792 QgsExpressionContext subContext;
9793 if ( context )
9794 subContext = *context;
9797 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9798 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), QVariant(), true ) );
9799 subContext.appendScope( subScope );
9800
9801 args->at( 1 )->prepare( parent, &subContext );
9802
9803 return true;
9804}
9807 : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
9808 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9809 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
9810 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
9811 QStringLiteral( "Arrays" ) )
9812{
9813
9814}
9815
9817{
9818 bool isStatic = false;
9819
9820 QgsExpressionNode::NodeList *args = node->args();
9822 if ( args->count() < 2 )
9823 return false;
9824
9825 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9826 {
9827 isStatic = true;
9828 }
9829 return isStatic;
9830}
9831
9833{
9834 Q_UNUSED( node )
9835 QVariantList result;
9836
9837 if ( args->count() < 2 )
9838 // error
9839 return result;
9840
9841 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9842
9843 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9844 std::unique_ptr< QgsExpressionContext > tempContext;
9845 if ( !subContext )
9846 {
9847 tempContext = std::make_unique< QgsExpressionContext >();
9848 subContext = tempContext.get();
9849 }
9850
9852 subContext->appendScope( subScope );
9853
9854 int limit = 0;
9855 if ( args->count() >= 3 )
9856 {
9857 const QVariant limitVar = args->at( 2 )->eval( parent, context );
9858
9859 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
9860 {
9861 limit = limitVar.toInt();
9862 }
9863 else
9864 {
9865 return result;
9866 }
9867 }
9868
9869 for ( const QVariant &value : array )
9870 {
9871 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
9872 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
9873 {
9874 result << value;
9875
9876 if ( limit > 0 && limit == result.size() )
9877 break;
9878 }
9879 }
9880
9881 if ( context )
9882 delete subContext->popScope();
9883
9884 return result;
9885}
9886
9887QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9889 // This is a dummy function, all the real handling is in run
9890 Q_UNUSED( values )
9891 Q_UNUSED( context )
9892 Q_UNUSED( parent )
9893 Q_UNUSED( node )
9894
9895 Q_ASSERT( false );
9896 return QVariant();
9897}
9898
9900{
9901 QgsExpressionNode::NodeList *args = node->args();
9902
9903 if ( args->count() < 2 )
9904 // error
9905 return false;
9906
9907 args->at( 0 )->prepare( parent, context );
9908
9909 QgsExpressionContext subContext;
9910 if ( context )
9911 subContext = *context;
9912
9914 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9915 subContext.appendScope( subScope );
9916
9917 args->at( 1 )->prepare( parent, &subContext );
9918
9919 return true;
9922 : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
9923 QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
9924 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
9925 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9926 QStringLiteral( "General" ) )
9927{
9928
9929}
9930
9932{
9933 bool isStatic = false;
9934
9935 QgsExpressionNode::NodeList *args = node->args();
9936
9937 if ( args->count() < 3 )
9938 return false;
9939
9940 // We only need to check if the node evaluation is static, if both - name and value - are static.
9941 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9942 {
9943 QVariant name = args->at( 0 )->eval( parent, context );
9944 QVariant value = args->at( 1 )->eval( parent, context );
9946 // Temporarily append a new scope to provide the variable
9947 appendTemporaryVariable( context, name.toString(), value );
9948 if ( args->at( 2 )->isStatic( parent, context ) )
9949 isStatic = true;
9950 popTemporaryVariable( context );
9951 }
9952
9953 return isStatic;
9954}
9955
9957{
9958 Q_UNUSED( node )
9959 QVariant result;
9960
9961 if ( args->count() < 3 )
9962 // error
9963 return result;
9964
9965 QVariant name = args->at( 0 )->eval( parent, context );
9966 QVariant value = args->at( 1 )->eval( parent, context );
9967
9968 const QgsExpressionContext *updatedContext = context;
9969 std::unique_ptr< QgsExpressionContext > tempContext;
9970 if ( !updatedContext )
9971 {
9972 tempContext = std::make_unique< QgsExpressionContext >();
9973 updatedContext = tempContext.get();
9975
9976 appendTemporaryVariable( updatedContext, name.toString(), value );
9977 result = args->at( 2 )->eval( parent, updatedContext );
9978
9979 if ( context )
9980 popTemporaryVariable( updatedContext );
9981
9982 return result;
9983}
9984
9985QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9987 // This is a dummy function, all the real handling is in run
9988 Q_UNUSED( values )
9989 Q_UNUSED( context )
9990 Q_UNUSED( parent )
9991 Q_UNUSED( node )
9992
9993 Q_ASSERT( false );
9994 return QVariant();
9995}
9996
9998{
9999 QgsExpressionNode::NodeList *args = node->args();
10000
10001 if ( args->count() < 3 )
10002 // error
10003 return false;
10004
10005 QVariant name = args->at( 0 )->prepare( parent, context );
10006 QVariant value = args->at( 1 )->prepare( parent, context );
10007
10008 const QgsExpressionContext *updatedContext = context;
10009 std::unique_ptr< QgsExpressionContext > tempContext;
10010 if ( !updatedContext )
10011 {
10012 tempContext = std::make_unique< QgsExpressionContext >();
10013 updatedContext = tempContext.get();
10014 }
10015
10016 appendTemporaryVariable( updatedContext, name.toString(), value );
10017 args->at( 2 )->prepare( parent, updatedContext );
10018
10019 if ( context )
10020 popTemporaryVariable( updatedContext );
10021
10022 return true;
10023}
10024
10025void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
10026{
10027 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10028 delete updatedContext->popScope();
10029}
10030
10031void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
10032{
10035
10036 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10037 updatedContext->appendScope( scope );
10038}
@ Left
Buffer to left of line.
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3185
@ ScaleDashOnly
Only dash lengths are adjusted.
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
@ ScaleGapOnly
Only gap lengths are adjusted.
@ Success
Operation succeeded.
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
JoinStyle
Join styles for buffers.
Definition qgis.h:2084
@ Bevel
Use beveled joins.
@ Round
Use rounded joins.
@ Miter
Use mitered joins.
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:5713
@ StdDev
Standard deviation.
@ NoStatistic
No statistic.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2071
@ Flat
Flat cap (in line with start/end of line)
@ Round
Round cap.
@ Square
Square cap (extends past start/end of line by buffer distance)
Aggregate
Available aggregates to calculate.
Definition qgis.h:5590
@ StringMinimumLength
Minimum length of string (string fields only)
@ FirstQuartile
First quartile (numeric fields only)
@ Mean
Mean of values (numeric fields only)
@ Median
Median of values (numeric fields only)
@ Max
Max of values.
@ Min
Min of values.
@ StringMaximumLength
Maximum length of string (string fields only)
@ Range
Range of values (max - min) (numeric and datetime fields only)
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
@ Sum
Sum of values.
@ Minority
Minority of values.
@ CountMissing
Number of missing (null) values.
@ ArrayAggregate
Create an array of values.
@ Majority
Majority of values.
@ StDevSample
Sample standard deviation of values (numeric fields only)
@ ThirdQuartile
Third quartile (numeric fields only)
@ CountDistinct
Number of distinct values.
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only)
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3170
@ HalfDash
Start or finish the pattern with a half length dash.
@ HalfGap
Start or finish the pattern with a half length gap.
@ FullGap
Start or finish the pattern with a full gap.
@ FullDash
Start or finish the pattern with a full dash.
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2130
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
@ PointM
PointM.
@ PointZ
PointZ.
@ GeometryCollection
GeometryCollection.
@ PointZM
PointZM.
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static Qgis::Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Handles the array_filter(array, expression) expression function.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
Handles the array loopingarray_Foreach(array, expression) expression function.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Circle geometry type.
Definition qgscircle.h:45
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
QFlags< FormatFlag > FormatFlags
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool isEmpty() const override
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
double roundness() const
Returns the roundness of the curve polygon.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Abstract base class for curved geometry type.
Definition qgscurve.h:35
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition qgscurve.cpp:277
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:53
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:272
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QVariantMap config() const
Ellipse geometry type.
Definition qgsellipse.h:39
QString what() const
Contains utilities for working with EXIF tags in images.
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
QString uniqueHash(bool &ok, const QSet< QString > &variables=QSet< QString >()) const
Returns a unique hash representing the current state of the context.
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
Represents a single parameter passed to a function.
A abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
bool lazyEval() const
true if this function should use lazy evaluation.
static bool allParamsStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context)
This will return true if all the params for the provided function node are static within the constrai...
QString name() const
The name of the function.
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function's...
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
virtual bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
This will be called during the prepare step() of an expression if it is not static.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes it value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for literal values.
A list of expression nodes.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
int count() const
Returns the number of nodes in the list.
Abstract base class for all nodes that can appear in an expression.
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
A set of expression-related functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
QString expression() const
Returns the original, unmodified expression string.
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Qgis::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
static PRIVATE QString helpText(QString name)
Returns the help text for a specified function.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
Qgis::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsFields fields
Definition qgsfeature.h:68
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthNotSet
Constraint is not set.
@ ConstraintStrengthSoft
User is warned if constraint is violated but feature can still be accepted.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QString name
Definition qgsfield.h:62
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:746
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
A geometry is the spatial representation of a feature.
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry squareWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs square waves along the boundary of the geometry, with the specified wavelength and amplitu...
QgsGeometry triangularWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs triangular waves along the boundary of the geometry, with the specified wavelength and amp...
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
QgsGeometry roundWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs rounded (sine-like) waves along the boundary of the geometry, with the specified wavelengt...
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const
Attempts to make an invalid geometry valid without losing vertices.
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static Q_INVOKABLE QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
Qgis::GeometryType type
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false) const
Returns a possibly concave polygon that contains all the points in the geometry.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:139
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2806
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
A representation of the interval between two datetime values.
Definition qgsinterval.h:46
bool isValid() const
Returns true if the interval is valid.
double days() const
Returns the interval duration in days.
double weeks() const
Returns the interval duration in weeks.
double months() const
Returns the interval duration in months (based on a 30 day month).
double seconds() const
Returns the interval duration in seconds.
double years() const
Returns the interval duration in years (based on an average year length)
double hours() const
Returns the interval duration in hours.
double minutes() const
Returns the interval duration in minutes.
QStringList rights() const
Returns a list of attribution or copyright strings associated with the resource.
Line string geometry type, with support for z-dimension and m-values.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Base class for all map layer types.
Definition qgsmaplayer.h:76
QString name
Definition qgsmaplayer.h:80
virtual QgsRectangle extent() const
Returns the extent of the layer.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:79
QgsLayerMetadata metadata
Definition qgsmaplayer.h:82
Qgis::LayerType type
Definition qgsmaplayer.h:86
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
virtual bool isEditable() const
Returns true if the layer can be edited.
double minimumScale() const
Returns the minimum map scale (i.e.
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double maximumScale() const
Returns the maximum map scale (i.e.
QString mapTipTemplate
Definition qgsmaplayer.h:89
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
A class to represent a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:242
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:692
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:558
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition qgspoint.cpp:426
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:105
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
double m
Definition qgspoint.h:55
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:704
double y
Definition qgspoint.h:53
QgsRelationManager * relationManager
Definition qgsproject.h:117
static QgsProject * instance()
Returns the QgsProject singleton instance.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Quadrilateral geometry type.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2)
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode)
Construct a QgsQuadrilateral as a Rectangle from 3 points.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
The Field class represents a Raster Attribute Table field, including its name, usage and type.
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double sum
The sum of all cells in the band. NO_DATA values are excluded.
double maximumValue
The maximum cell value in the raster band.
double range
The range is the distance between min & max.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
void grow(double delta)
Grows the rectangle in place by the specified amount.
double yMaximum
QgsPointXY center
Regular Polygon geometry type.
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
@ CircumscribedCircle
Circumscribed about a circle (the radius is the distance from the center to the midpoints of the side...
@ InscribedCircle
Inscribed in a circle (the radius is the distance between the center and vertices)
QgsPolygon * toPolygon() const
Returns as a polygon.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
Definition qgsrelation.h:44
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:49
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:48
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
c++ helper class for defining QgsExpression functions.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
void setIsStaticFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *) > &isStatic)
Set a function that will be called in the prepare step to determine if the function is static or not.
QStringList aliases() const override
Returns a list of possible aliases for the function.
void setPrepareFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *)> &prepareFunc)
Set a function that will be called in the prepare step to determine if the function is static or not.
void setUsesGeometryFunction(const std::function< bool(const QgsExpressionNodeFunction *node)> &usesGeometry)
Set a function that will be called when determining if the function requires feature geometry or not.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
QgsStaticExpressionFunction(const QString &fnname, int params, FcnEval fcn, const QString &group, const QString &helpText=QString(), bool usesGeometry=false, const QSet< QString > &referencedColumns=QSet< QString >(), bool lazyEval=false, const QStringList &aliases=QStringList(), bool handlesNull=false)
Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter val...
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
Utility functions for working with strings.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance)
Definition qgsstyle.cpp:501
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:146
Contains utility functions for working with symbols and symbol layers.
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
This class allows including a set of layers in a database-side transaction, provided the layer data p...
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based data sets.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QVariant aggregate(Qgis::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
Handles the with_variable(name, value, node) expression function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
CORE_EXPORT QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
CORE_EXPORT QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp:121
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6796
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6795
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:6260
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6219
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QList< QgsExpressionFunction * > ExpressionFunctionList
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColorObject(const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction *)
bool(QgsGeometry::* RelationFunction)(const QgsGeometry &geometry) const
#define ENSURE_NO_EVAL_ERROR
#define FEAT_FROM_CONTEXT(c, f)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Definition qgsgeometry.h:84
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:80
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Definition qgsgeometry.h:91
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
QLineF segment(int index, QRectF rect, double radius)
const QgsCoordinateReferenceSystem & crs
int precision
A bundle of parameters controlling aggregate calculation.
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
Single variable definition for use within a QgsExpressionContextScope.
The Context struct stores the current layer and coordinate transform context.
Definition qgsogcutils.h:62
const QgsMapLayer * layer
Definition qgsogcutils.h:72
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:73
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30