QGIS API Documentation 3.99.0-Master (8e76e220402)
Loading...
Searching...
No Matches
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
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
18
19#include <random>
20#include <sqlite3.h>
21
22#include "qgis.h"
23#include "qgsapplication.h"
24#include "qgscolorramp.h"
25#include "qgscolorrampimpl.h"
27#include "qgscoordinateutils.h"
28#include "qgscurve.h"
29#include "qgscurvepolygon.h"
30#include "qgsdistancearea.h"
31#include "qgsexception.h"
32#include "qgsexiftools.h"
36#include "qgsexpressionutils.h"
37#include "qgsfeaturerequest.h"
38#include "qgsfieldformatter.h"
40#include "qgsgeometryengine.h"
41#include "qgsgeometryutils.h"
42#include "qgsgeos.h"
43#include "qgshstoreutils.h"
44#include "qgslinestring.h"
45#include "qgsmagneticmodel.h"
47#include "qgsmessagelog.h"
48#include "qgsmultilinestring.h"
49#include "qgsmultipoint.h"
50#include "qgsogcutils.h"
51#include "qgspolygon.h"
52#include "qgsproviderregistry.h"
53#include "qgsquadrilateral.h"
54#include "qgsrasterbandstats.h"
55#include "qgsrasterlayer.h"
56#include "qgsregularpolygon.h"
57#include "qgsspatialindex.h"
58#include "qgsstringutils.h"
59#include "qgsstyle.h"
60#include "qgssymbollayerutils.h"
61#include "qgsthreadingutils.h"
62#include "qgstransaction.h"
63#include "qgstriangle.h"
64#include "qgsunittypes.h"
65#include "qgsvariantutils.h"
66#include "qgsvectorlayer.h"
68#include "qgsvectorlayerutils.h"
69
70#include <QCryptographicHash>
71#include <QMimeDatabase>
72#include <QProcessEnvironment>
73#include <QRegularExpression>
74#include <QString>
75#include <QUrlQuery>
76#include <QUuid>
77
78using namespace Qt::StringLiterals;
79
80typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
81
83Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
85
88Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
89
90const QString QgsExpressionFunction::helpText() const
91{
92 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
93}
94
96{
97 Q_UNUSED( node )
98 // evaluate arguments
99 QVariantList argValues;
100 if ( args )
101 {
102 int arg = 0;
103 const QList< QgsExpressionNode * > argList = args->list();
104 argValues.reserve( argList.size() );
105 for ( QgsExpressionNode *n : argList )
106 {
107 QVariant v;
108 if ( lazyEval() )
109 {
110 // Pass in the node for the function to eval as it needs.
111 v = QVariant::fromValue( n );
112 }
113 else
114 {
115 v = n->eval( parent, context );
117 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
118 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
119 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
120 }
121 argValues.append( v );
122 arg++;
123 }
124 }
125
126 return func( argValues, context, parent, node );
127}
128
130{
131 Q_UNUSED( node )
132 return true;
133}
134
136{
137 return QStringList();
138}
139
141{
142 Q_UNUSED( parent )
143 Q_UNUSED( context )
144 Q_UNUSED( node )
145 return false;
146}
147
149{
150 Q_UNUSED( parent )
151 Q_UNUSED( context )
152 Q_UNUSED( node )
153 return true;
154}
155
157{
158 Q_UNUSED( node )
159 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
160}
161
163{
164 return mGroups.isEmpty() ? false : mGroups.contains( u"deprecated"_s );
165}
166
168{
169 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
170}
171
173{
174 return mHandlesNull;
175}
176
177// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
180 FcnEval fcn,
181 const QString &group,
182 const QString &helpText,
183 const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
184 const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
185 bool lazyEval,
186 const QStringList &aliases,
187 bool handlesNull )
188 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
189 , mFnc( fcn )
190 , mAliases( aliases )
191 , mUsesGeometry( false )
192 , mUsesGeometryFunc( usesGeometry )
193 , mReferencedColumnsFunc( referencedColumns )
194{
195}
197
199{
200 return mAliases;
201}
202
204{
205 if ( mUsesGeometryFunc )
206 return mUsesGeometryFunc( node );
207 else
208 return mUsesGeometry;
209}
210
212{
213 mUsesGeometryFunc = usesGeometry;
214}
215
217{
218 if ( mReferencedColumnsFunc )
219 return mReferencedColumnsFunc( node );
220 else
221 return mReferencedColumns;
222}
223
225{
226 if ( mIsStaticFunc )
227 return mIsStaticFunc( node, parent, context );
228 else
229 return mIsStatic;
230}
231
233{
234 if ( mPrepareFunc )
235 return mPrepareFunc( node, parent, context );
236
237 return true;
238}
239
241{
242 mIsStaticFunc = isStatic;
243}
244
246{
247 mIsStaticFunc = nullptr;
248 mIsStatic = isStatic;
249}
250
251void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
252{
253 mPrepareFunc = prepareFunc;
254}
255
257{
258 if ( node && node->args() )
259 {
260 const QList< QgsExpressionNode * > argList = node->args()->list();
261 for ( QgsExpressionNode *argNode : argList )
262 {
263 if ( !argNode->isStatic( parent, context ) )
264 return false;
265 }
266 }
267
268 return true;
269}
270
271static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
272{
273 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
274 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
275 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
276
277 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
278 return QVariant();
279
280 QVariantList array;
281 int length = 1;
282
283 array << start;
284 double current = start + step;
285 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
286 {
287 array << current;
288 current += step;
289 length++;
290 }
291
292 return array;
293}
294
295static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
296{
297 if ( !context )
298 return QVariant();
299
300 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
301
302 if ( name == "feature"_L1 )
303 {
304 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
305 }
306 else if ( name == "id"_L1 )
307 {
308 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
309 }
310 else if ( name == "geometry"_L1 )
311 {
312 if ( !context->hasFeature() )
313 return QVariant();
314
315 const QgsFeature feature = context->feature();
316 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
317 }
318 else
319 {
320 return context->variable( name );
321 }
322}
323
324static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
325{
326 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
327 return QgsExpression::replaceExpressionText( templateString, context );
328}
329
330static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
331{
332 if ( !context )
333 return QVariant();
334
335 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
336 QgsExpression expression( expString );
337 return expression.evaluate( context );
338}
339
340static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
341{
342 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
343 return QVariant( std::sqrt( x ) );
344}
345
346static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
347{
348 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
349 return QVariant( std::fabs( val ) );
350}
351
352static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
353{
354 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
355 return ( deg * M_PI ) / 180;
356}
357static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
358{
359 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
360 return ( 180 * rad ) / M_PI;
361}
362static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
363{
364 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
365 return QVariant( std::sin( x ) );
366}
367static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
368{
369 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
370 return QVariant( std::cos( x ) );
371}
372static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
373{
374 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
375 return QVariant( std::tan( x ) );
376}
377static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
378{
379 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
380 return QVariant( std::asin( x ) );
381}
382static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
383{
384 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
385 return QVariant( std::acos( x ) );
386}
387static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
388{
389 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
390 return QVariant( std::atan( x ) );
391}
392static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
393{
394 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
395 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
396 return QVariant( std::atan2( y, x ) );
397}
398static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
399{
400 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
401 return QVariant( std::exp( x ) );
402}
403static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
404{
405 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
406 if ( x <= 0 )
407 return QVariant();
408 return QVariant( std::log( x ) );
409}
410static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
411{
412 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
413 if ( x <= 0 )
414 return QVariant();
415 return QVariant( log10( x ) );
416}
417static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
418{
419 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
420 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
421 if ( x <= 0 || b <= 0 )
422 return QVariant();
423 return QVariant( std::log( x ) / std::log( b ) );
424}
425static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
426{
427 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
428 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
429 if ( max < min )
430 return QVariant();
431
432 std::random_device rd;
433 std::mt19937_64 generator( rd() );
434
435 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
436 {
437 quint32 seed;
438 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
439 {
440 // if seed can be converted to int, we use as is
441 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
442 }
443 else
444 {
445 // if not, we hash string representation to int
446 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
447 std::hash<std::string> hasher;
448 seed = hasher( seedStr.toStdString() );
449 }
450 generator.seed( seed );
451 }
452
453 // Return a random double in the range [min, max] (inclusive)
454 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
455 return QVariant( min + f * ( max - min ) );
456}
457static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
458{
459 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
460 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
461 if ( max < min )
462 return QVariant();
463
464 std::random_device rd;
465 std::mt19937_64 generator( rd() );
466
467 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
468 {
469 quint32 seed;
470 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
471 {
472 // if seed can be converted to int, we use as is
473 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
474 }
475 else
476 {
477 // if not, we hash string representation to int
478 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
479 std::hash<std::string> hasher;
480 seed = hasher( seedStr.toStdString() );
481 }
482 generator.seed( seed );
483 }
484
485 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
486 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
487 return QVariant( randomInteger );
488
489 // Prevent wrong conversion of QVariant. See #36412
490 return QVariant( int( randomInteger ) );
491}
492
493static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
494{
495 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
496 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
497 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
498 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
499 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
500
501 if ( domainMin >= domainMax )
502 {
503 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
504 return QVariant();
505 }
506
507 // outside of domain?
508 if ( val >= domainMax )
509 {
510 return rangeMax;
511 }
512 else if ( val <= domainMin )
513 {
514 return rangeMin;
515 }
516
517 // calculate linear scale
518 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
519 double c = rangeMin - ( domainMin * m );
520
521 // Return linearly scaled value
522 return QVariant( m * val + c );
523}
524
525static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
526{
527 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
528 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
529 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
530 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
531 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
532 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
533
534 if ( domainMin >= domainMax )
535 {
536 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
537 return QVariant();
538 }
539 if ( exponent <= 0 )
540 {
541 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
542 return QVariant();
543 }
544
545 // outside of domain?
546 if ( val >= domainMax )
547 {
548 return rangeMax;
549 }
550 else if ( val <= domainMin )
551 {
552 return rangeMin;
553 }
554
555 // Return polynomially scaled value
556 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
557}
558
559static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
560{
561 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
562 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
563 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
564 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
565 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
566 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
567
568 if ( domainMin >= domainMax )
569 {
570 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
571 return QVariant();
572 }
573 if ( exponent <= 0 )
574 {
575 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
576 return QVariant();
577 }
578
579 // outside of domain?
580 if ( val >= domainMax )
581 {
582 return rangeMax;
583 }
584 else if ( val <= domainMin )
585 {
586 return rangeMin;
587 }
588
589 // Return exponentially scaled value
590 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
591 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
592}
593
594static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
595{
596 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
597 double maxVal = std::numeric_limits<double>::quiet_NaN();
598 for ( const QVariant &val : values )
599 {
600 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
601 if ( std::isnan( maxVal ) )
602 {
603 maxVal = testVal;
604 }
605 else if ( !std::isnan( testVal ) )
606 {
607 maxVal = std::max( maxVal, testVal );
608 }
609 }
610
611 if ( !std::isnan( maxVal ) )
612 {
613 result = QVariant( maxVal );
614 }
615 return result;
616}
617
618static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
619{
620 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
621 double minVal = std::numeric_limits<double>::quiet_NaN();
622 for ( const QVariant &val : values )
623 {
624 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
625 if ( std::isnan( minVal ) )
626 {
627 minVal = testVal;
628 }
629 else if ( !std::isnan( testVal ) )
630 {
631 minVal = std::min( minVal, testVal );
632 }
633 }
634
635 if ( !std::isnan( minVal ) )
636 {
637 result = QVariant( minVal );
638 }
639 return result;
640}
641
642static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
643{
644 //lazy eval, so we need to evaluate nodes now
645
646 //first node is layer id or name
647 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
649 QVariant value = node->eval( parent, context );
651
652 // TODO this expression function is NOT thread safe
654 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
656 if ( !vl )
657 {
658 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
659 return QVariant();
660 }
661
662 // second node is aggregate type
663 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
665 value = node->eval( parent, context );
667 bool ok = false;
668 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
669 if ( !ok )
670 {
671 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
672 return QVariant();
673 }
674
675 // third node is subexpression (or field name)
676 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
678 QString subExpression = node->dump();
679
681 //optional forth node is filter
682 if ( values.count() > 3 )
683 {
684 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
686 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
687 if ( !nl || nl->value().isValid() )
688 parameters.filter = node->dump();
689 }
690
691 //optional fifth node is concatenator
692 if ( values.count() > 4 )
693 {
694 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
696 value = node->eval( parent, context );
698 parameters.delimiter = value.toString();
699 }
700
701 //optional sixth node is order by
702 QString orderBy;
703 if ( values.count() > 5 )
704 {
705 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
707 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
708 if ( !nl || nl->value().isValid() )
709 {
710 orderBy = node->dump();
711 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
712 }
713 }
714
715 QString aggregateError;
716 QVariant result;
717 if ( context )
718 {
719 QString cacheKey;
720 QgsExpression subExp( subExpression );
721 QgsExpression filterExp( parameters.filter );
722
723 const QSet< QString > filterVars = filterExp.referencedVariables();
724 const QSet< QString > subExpVars = subExp.referencedVariables();
725 QSet<QString> allVars = filterVars + subExpVars;
726
727 bool isStatic = true;
728 if ( filterVars.contains( u"parent"_s )
729 || filterVars.contains( QString() )
730 || subExpVars.contains( u"parent"_s )
731 || subExpVars.contains( QString() ) )
732 {
733 isStatic = false;
734 }
735 else
736 {
737 for ( const QString &varName : allVars )
738 {
739 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
740 if ( scope && !scope->isStatic( varName ) )
741 {
742 isStatic = false;
743 break;
744 }
745 }
746 }
747
748 if ( isStatic && ! parameters.orderBy.isEmpty() )
749 {
750 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
751 {
752 const QgsExpression &orderByExpression { orderByClause.expression() };
753 if ( orderByExpression.referencedVariables().contains( u"parent"_s ) || orderByExpression.referencedVariables().contains( QString() ) )
754 {
755 isStatic = false;
756 break;
757 }
758 }
759 }
760
761 if ( !isStatic )
762 {
763 bool ok = false;
764 const QString contextHash = context->uniqueHash( ok, allVars );
765 if ( ok )
766 {
767 cacheKey = u"aggfcn:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
768 orderBy, contextHash );
769 }
770 }
771 else
772 {
773 cacheKey = u"aggfcn:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
774 }
775
776 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
777 {
778 return context->cachedValue( cacheKey );
779 }
780
781 QgsExpressionContext subContext( *context );
783 subScope->setVariable( u"parent"_s, context->feature(), true );
784 subContext.appendScope( subScope );
785 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
786
787 if ( ok && !cacheKey.isEmpty() )
788 {
789 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
790 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
791 // associated with it's calculation!
792 context->setCachedValue( cacheKey, result );
793 }
794 }
795 else
796 {
797 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
798 }
799 if ( !ok )
800 {
801 if ( !aggregateError.isEmpty() )
802 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
803 else
804 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
805 return QVariant();
806 }
807
808 return result;
809}
810
811static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
812{
813 if ( !context )
814 {
815 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
816 return QVariant();
817 }
818
819 // first step - find current layer
820
821 // TODO this expression function is NOT thread safe
823 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
825 if ( !vl )
826 {
827 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
828 return QVariant();
829 }
830
831 //lazy eval, so we need to evaluate nodes now
832
833 //first node is relation name
834 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
836 QVariant value = node->eval( parent, context );
838 QString relationId = value.toString();
839 // check relation exists
840 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
841 if ( !relation.isValid() || relation.referencedLayer() != vl )
842 {
843 // check for relations by name
844 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
845 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
846 {
847 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
848 return QVariant();
849 }
850 else
851 {
852 relation = relations.at( 0 );
853 }
854 }
855
856 QgsVectorLayer *childLayer = relation.referencingLayer();
857
858 // second node is aggregate type
859 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
861 value = node->eval( parent, context );
863 bool ok = false;
864 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
865 if ( !ok )
866 {
867 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
868 return QVariant();
869 }
870
871 //third node is subexpression (or field name)
872 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
874 QString subExpression = node->dump();
875
876 //optional fourth node is concatenator
878 if ( values.count() > 3 )
879 {
880 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
882 value = node->eval( parent, context );
884 parameters.delimiter = value.toString();
885 }
886
887 //optional fifth node is order by
888 QString orderBy;
889 if ( values.count() > 4 )
890 {
891 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
893 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
894 if ( !nl || nl->value().isValid() )
895 {
896 orderBy = node->dump();
897 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
898 }
899 }
900
901 if ( !context->hasFeature() )
902 return QVariant();
903 QgsFeature f = context->feature();
904
905 parameters.filter = relation.getRelatedFeaturesFilter( f );
906
907 const QString cacheKey = u"relagg:%1%:%2:%3:%4:%5:%6"_s.arg( relationId, vl->id(),
908 QString::number( static_cast< int >( aggregate ) ),
909 subExpression,
910 parameters.filter,
911 orderBy );
912 if ( context->hasCachedValue( cacheKey ) )
913 return context->cachedValue( cacheKey );
914
915 QVariant result;
916 ok = false;
917
918
919 QgsExpressionContext subContext( *context );
920 QString error;
921 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
922
923 if ( !ok )
924 {
925 if ( !error.isEmpty() )
926 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
927 else
928 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
929 return QVariant();
930 }
931
932 // cache value
933 context->setCachedValue( cacheKey, result );
934 return result;
935}
936
937
938static QVariant fcnAggregateGeneric( Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
939{
940 if ( !context )
941 {
942 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
943 return QVariant();
944 }
945
946 // first step - find current layer
947
948 // TODO this expression function is NOT thread safe
950 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
952 if ( !vl )
953 {
954 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
955 return QVariant();
956 }
957
958 //lazy eval, so we need to evaluate nodes now
959
960 //first node is subexpression (or field name)
961 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
963 QString subExpression = node->dump();
964
965 //optional second node is group by
966 QString groupBy;
967 if ( values.count() > 1 )
968 {
969 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
971 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
972 if ( !nl || nl->value().isValid() )
973 groupBy = node->dump();
974 }
975
976 //optional third node is filter
977 if ( values.count() > 2 )
978 {
979 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
981 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
982 if ( !nl || nl->value().isValid() )
983 parameters.filter = node->dump();
984 }
985
986 //optional order by node, if supported
987 QString orderBy;
988 if ( orderByPos >= 0 && values.count() > orderByPos )
989 {
990 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
992 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
993 if ( !nl || nl->value().isValid() )
994 {
995 orderBy = node->dump();
996 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
997 }
998 }
999
1000 // build up filter with group by
1001
1002 // find current group by value
1003 if ( !groupBy.isEmpty() )
1004 {
1005 QgsExpression groupByExp( groupBy );
1006 QVariant groupByValue = groupByExp.evaluate( context );
1007 QString groupByClause = u"%1 %2 %3"_s.arg( groupBy,
1008 QgsVariantUtils::isNull( groupByValue ) ? u"is"_s : u"="_s,
1009 QgsExpression::quotedValue( groupByValue ) );
1010 if ( !parameters.filter.isEmpty() )
1011 parameters.filter = u"(%1) AND (%2)"_s.arg( parameters.filter, groupByClause );
1012 else
1013 parameters.filter = groupByClause;
1014 }
1015
1016 QgsExpression subExp( subExpression );
1017 QgsExpression filterExp( parameters.filter );
1018
1019 bool isStatic = true;
1020 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1021 for ( const QString &varName : refVars )
1022 {
1023 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1024 if ( scope && !scope->isStatic( varName ) )
1025 {
1026 isStatic = false;
1027 break;
1028 }
1029 }
1030
1031 QString cacheKey;
1032 if ( !isStatic )
1033 {
1034 bool ok = false;
1035 const QString contextHash = context->uniqueHash( ok, refVars );
1036 if ( ok )
1037 {
1038 cacheKey = u"agg:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
1039 orderBy, contextHash );
1040 }
1041 }
1042 else
1043 {
1044 cacheKey = u"agg:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1045 }
1046
1047 if ( context->hasCachedValue( cacheKey ) )
1048 return context->cachedValue( cacheKey );
1049
1050 QVariant result;
1051 bool ok = false;
1052
1053 QgsExpressionContext subContext( *context );
1055 subScope->setVariable( u"parent"_s, context->feature(), true );
1056 subContext.appendScope( subScope );
1057 QString error;
1058 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1059
1060 if ( !ok )
1061 {
1062 if ( !error.isEmpty() )
1063 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1064 else
1065 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1066 return QVariant();
1067 }
1068
1069 // cache value
1070 context->setCachedValue( cacheKey, result );
1071 return result;
1072}
1073
1074
1075static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1076{
1077 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1078}
1079
1080static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1081{
1082 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1083}
1084
1085static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1086{
1087 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1088}
1089
1090static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1091{
1092 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1093}
1094
1095static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1096{
1097 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1098}
1099
1100static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1101{
1102 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1103}
1104
1105static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1106{
1107 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1108}
1109
1110static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1111{
1112 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1113}
1114
1115static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1116{
1117 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1118}
1119
1120static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1121{
1122 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1123}
1124
1125static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1126{
1127 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1128}
1129
1130static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1131{
1132 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1133}
1134
1135static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1136{
1137 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1138}
1139
1140static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1141{
1142 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1143}
1144
1145static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1146{
1147 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1148}
1149
1150static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1151{
1152 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1153}
1154
1155static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1156{
1157 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1158}
1159
1160static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1161{
1162 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1163}
1164
1165static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1166{
1168
1169 //fourth node is concatenator
1170 if ( values.count() > 3 )
1171 {
1172 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1174 QVariant value = node->eval( parent, context );
1176 parameters.delimiter = value.toString();
1177 }
1178
1179 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1180}
1181
1182static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1183{
1185
1186 //fourth node is concatenator
1187 if ( values.count() > 3 )
1188 {
1189 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1191 QVariant value = node->eval( parent, context );
1193 parameters.delimiter = value.toString();
1194 }
1195
1196 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1197}
1198
1199static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1200{
1201 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1202}
1203
1204static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1205{
1206 if ( !context )
1207 return QVariant();
1208
1209 QVariant scale = context->variable( u"map_scale"_s );
1210 bool ok = false;
1211 if ( QgsVariantUtils::isNull( scale ) )
1212 return QVariant();
1213
1214 const double v = scale.toDouble( &ok );
1215 if ( ok )
1216 return v;
1217 return QVariant();
1218}
1219
1220static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1221{
1222 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1223 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1224 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1225
1226 // force testValue to sit inside the range specified by the min and max value
1227 if ( testValue <= minValue )
1228 {
1229 return QVariant( minValue );
1230 }
1231 else if ( testValue >= maxValue )
1232 {
1233 return QVariant( maxValue );
1234 }
1235 else
1236 {
1237 return QVariant( testValue );
1238 }
1239}
1240
1241static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1242{
1243 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1244 return QVariant( std::floor( x ) );
1245}
1246
1247static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1248{
1249 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1250 return QVariant( std::ceil( x ) );
1251}
1252
1253static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1254{
1255 const QVariant value = values.at( 0 );
1256 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1257 {
1258 return QVariant( false );
1259 }
1260 else if ( value.userType() == QMetaType::QString )
1261 {
1262 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1263 return QVariant( !value.toString().isEmpty() );
1264 }
1265 else if ( QgsExpressionUtils::isList( value ) )
1266 {
1267 return !value.toList().isEmpty();
1268 }
1269 return QVariant( value.toBool() );
1270}
1271static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1272{
1273 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1274}
1275static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1276{
1277 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1278}
1279static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1280{
1281 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1282}
1283
1284static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1285{
1286 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1287 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1288 if ( format.isEmpty() && !language.isEmpty() )
1289 {
1290 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1291 return QVariant( QDateTime() );
1292 }
1293
1294 if ( format.isEmpty() && language.isEmpty() )
1295 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1296
1297 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1298 QLocale locale = QLocale();
1299 if ( !language.isEmpty() )
1300 {
1301 locale = QLocale( language );
1302 }
1303
1304 QDateTime datetime = locale.toDateTime( datetimestring, format );
1305 if ( !datetime.isValid() )
1306 {
1307 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1308 datetime = QDateTime();
1309 }
1310 return QVariant( datetime );
1311}
1312
1313static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1314{
1315 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1316 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1317 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1318
1319 const QDate date( year, month, day );
1320 if ( !date.isValid() )
1321 {
1322 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1323 return QVariant();
1324 }
1325 return QVariant( date );
1326}
1327
1328static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1329{
1330 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1331 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1332 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1333
1334 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1335 if ( !time.isValid() )
1336 {
1337 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1338 return QVariant();
1339 }
1340 return QVariant( time );
1341}
1342
1343static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1344{
1345 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1346 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1347 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1348 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1349 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1350 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1351
1352 const QDate date( year, month, day );
1353 if ( !date.isValid() )
1354 {
1355 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1356 return QVariant();
1357 }
1358 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1359 if ( !time.isValid() )
1360 {
1361 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1362 return QVariant();
1363 }
1364 return QVariant( QDateTime( date, time ) );
1365}
1366
1367static QVariant fcnTimeZoneFromId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1368{
1369 const QString timeZoneId = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1370
1371 QTimeZone tz;
1372
1373#if QT_FEATURE_timezone > 0
1374 if ( !timeZoneId.isEmpty() )
1375 {
1376 tz = QTimeZone( timeZoneId.toUtf8() );
1377 }
1378
1379 if ( !tz.isValid() )
1380 {
1381 parent->setEvalErrorString( QObject::tr( "'%1' is not a valid time zone ID" ).arg( timeZoneId ) );
1382 return QVariant();
1383 }
1384
1385#else
1386 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneFromId" ) );
1387#endif
1388 return QVariant::fromValue( tz );
1389}
1390
1391static QVariant fcnGetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1392{
1393#if QT_FEATURE_timezone > 0
1394 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1395 if ( datetime.isValid() )
1396 {
1397 return QVariant::fromValue( datetime.timeZone() );
1398 }
1399 return QVariant();
1400#else
1401 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnGetTimeZone" ) );
1402 return QVariant();
1403#endif
1404}
1405
1406static QVariant fcnSetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1407{
1408#if QT_FEATURE_timezone > 0
1409 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1410 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1411 if ( datetime.isValid() && tz.isValid() )
1412 {
1413 datetime.setTimeZone( tz );
1414 return QVariant::fromValue( datetime );
1415 }
1416 return QVariant();
1417#else
1418 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnSetTimeZone" ) );
1419 return QVariant();
1420#endif
1421}
1422
1423static QVariant fcnConvertTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1424{
1425#if QT_FEATURE_timezone > 0
1426 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1427 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1428 if ( datetime.isValid() && tz.isValid() )
1429 {
1430 return QVariant::fromValue( datetime.toTimeZone( tz ) );
1431 }
1432 return QVariant();
1433#else
1434 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnConvertTimeZone" ) );
1435 return QVariant();
1436#endif
1437}
1438
1439static QVariant fcnTimeZoneToId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1440{
1441#if QT_FEATURE_timezone > 0
1442 const QTimeZone timeZone = QgsExpressionUtils::getTimeZoneValue( values.at( 0 ), parent );
1443 if ( timeZone.isValid() )
1444 {
1445 return QString( timeZone.id() );
1446 }
1447 return QVariant();
1448#else
1449 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneToId" ) );
1450 return QVariant();
1451#endif
1452}
1453
1454static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1455{
1456 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1457 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1458 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1459 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1460 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1461 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1462 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1463
1464 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1465}
1466
1467static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1468{
1469 for ( const QVariant &value : values )
1470 {
1471 if ( QgsVariantUtils::isNull( value ) )
1472 continue;
1473 return value;
1474 }
1475 return QVariant();
1476}
1477
1478static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1479{
1480 const QVariant val1 = values.at( 0 );
1481 const QVariant val2 = values.at( 1 );
1482
1483 if ( val1 == val2 )
1484 return QVariant();
1485 else
1486 return val1;
1487}
1488
1489static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1490{
1491 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1492 return QVariant( str.toLower() );
1493}
1494static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1495{
1496 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1497 return QVariant( str.toUpper() );
1498}
1499static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1500{
1501 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1502 QStringList elems = str.split( ' ' );
1503 for ( int i = 0; i < elems.size(); i++ )
1504 {
1505 if ( elems[i].size() > 1 )
1506 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1507 }
1508 return QVariant( elems.join( ' '_L1 ) );
1509}
1510
1511static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1512{
1513 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1514 return QVariant( str.trimmed() );
1515}
1516
1517static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1518{
1519 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1520
1521 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1522
1523 const QRegularExpression re( u"^([%1]*)"_s.arg( QRegularExpression::escape( characters ) ) );
1524 str.replace( re, QString() );
1525 return QVariant( str );
1526}
1527
1528static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1529{
1530 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1531
1532 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1533
1534 const QRegularExpression re( u"([%1]*)$"_s.arg( QRegularExpression::escape( characters ) ) );
1535 str.replace( re, QString() );
1536 return QVariant( str );
1537}
1538
1539static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1540{
1541 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1542 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1543 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1544}
1545
1546static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1547{
1548 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1549 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1550 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1551}
1552
1553static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1554{
1555 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1556 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1557 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1558 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1559}
1560
1561static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1562{
1563 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1564 return QVariant( QgsStringUtils::soundex( string ) );
1565}
1566
1567static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1568{
1569 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1570 return QVariant( QString( character ) );
1571}
1572
1573static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1574{
1575 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1576
1577 if ( value.isEmpty() )
1578 {
1579 return QVariant();
1580 }
1581
1582 int res = value.at( 0 ).unicode();
1583 return QVariant( res );
1584}
1585
1586static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1587{
1588 if ( values.length() == 2 || values.length() == 3 )
1589 {
1590 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1591 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1592
1593 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1594
1595 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1596 }
1597
1598 return QVariant();
1599}
1600
1601static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1602{
1603 // two variants, one for geometry, one for string
1604
1605 //geometry variant
1606 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1607 if ( !geom.isNull() )
1608 {
1609 if ( geom.type() == Qgis::GeometryType::Line )
1610 return QVariant( geom.length() );
1611 else
1612 return QVariant();
1613 }
1614
1615 //otherwise fall back to string variant
1616 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1617 return QVariant( str.length() );
1618}
1619
1620static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1621{
1622 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1623
1624 if ( geom.type() != Qgis::GeometryType::Line )
1625 return QVariant();
1626
1627 double totalLength = 0;
1628 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1629 {
1631 {
1632 totalLength += line->length3D();
1633 }
1634 else
1635 {
1636 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1637 totalLength += segmentized->length3D();
1638 }
1639 }
1640
1641 return totalLength;
1642}
1643
1644
1645static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1646{
1647 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1648 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1649 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1650}
1651
1652static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1653{
1654 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1655 {
1656 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1657 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1658 QVector< QPair< QString, QString > > mapItems;
1659
1660 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1661 {
1662 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1663 }
1664
1665 // larger keys should be replaced first since they may contain whole smaller keys
1666 std::sort( mapItems.begin(),
1667 mapItems.end(),
1668 []( const QPair< QString, QString > &pair1,
1669 const QPair< QString, QString > &pair2 )
1670 {
1671 return ( pair1.first.length() > pair2.first.length() );
1672 } );
1673
1674 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1675 {
1676 str = str.replace( it->first, it->second );
1677 }
1678
1679 return QVariant( str );
1680 }
1681 else if ( values.count() == 3 )
1682 {
1683 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1684 QVariantList before;
1685 QVariantList after;
1686 bool isSingleReplacement = false;
1687
1688 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1689 {
1690 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1691 }
1692 else
1693 {
1694 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1695 }
1696
1697 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1698 {
1699 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1700 isSingleReplacement = true;
1701 }
1702 else
1703 {
1704 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1705 }
1706
1707 if ( !isSingleReplacement && before.length() != after.length() )
1708 {
1709 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1710 return QVariant();
1711 }
1712
1713 for ( int i = 0; i < before.length(); i++ )
1714 {
1715 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1716 }
1717
1718 return QVariant( str );
1719 }
1720 else
1721 {
1722 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1723 return QVariant();
1724 }
1725}
1726
1727static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1728{
1729 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1730 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1731 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1732
1733 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1734 if ( !re.isValid() )
1735 {
1736 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1737 return QVariant();
1738 }
1739 return QVariant( str.replace( re, after ) );
1740}
1741
1742static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1743{
1744 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1745 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1746
1747 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1748 if ( !re.isValid() )
1749 {
1750 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1751 return QVariant();
1752 }
1753 return QVariant( ( str.indexOf( re ) + 1 ) );
1754}
1755
1756static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1757{
1758 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1759 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1760 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1761
1762 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1763 if ( !re.isValid() )
1764 {
1765 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1766 return QVariant();
1767 }
1768
1769 QRegularExpressionMatch matches = re.match( str );
1770 if ( matches.hasMatch() )
1771 {
1772 QVariantList array;
1773 QStringList list = matches.capturedTexts();
1774
1775 // Skip the first string to only return captured groups
1776 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1777 {
1778 array += ( !( *it ).isEmpty() ) ? *it : empty;
1779 }
1780
1781 return QVariant( array );
1782 }
1783 else
1784 {
1785 return QVariant();
1786 }
1787}
1788
1789static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1790{
1791 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1792 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1793
1794 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1795 if ( !re.isValid() )
1796 {
1797 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1798 return QVariant();
1799 }
1800
1801 // extract substring
1802 QRegularExpressionMatch match = re.match( str );
1803 if ( match.hasMatch() )
1804 {
1805 // return first capture
1806 if ( match.lastCapturedIndex() > 0 )
1807 {
1808 // a capture group was present, so use that
1809 return QVariant( match.captured( 1 ) );
1810 }
1811 else
1812 {
1813 // no capture group, so using all match
1814 return QVariant( match.captured( 0 ) );
1815 }
1816 }
1817 else
1818 {
1819 return QVariant( "" );
1820 }
1821}
1822
1823static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1824{
1825 QString uuid = QUuid::createUuid().toString();
1826 if ( values.at( 0 ).toString().compare( u"WithoutBraces"_s, Qt::CaseInsensitive ) == 0 )
1827 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1828 else if ( values.at( 0 ).toString().compare( u"Id128"_s, Qt::CaseInsensitive ) == 0 )
1829 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1830 return uuid;
1831}
1832
1833static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1834{
1835 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1836 return QVariant();
1837
1838 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1839 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1840
1841 int len = 0;
1842 if ( values.at( 2 ).isValid() )
1843 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1844 else
1845 len = str.size();
1846
1847 if ( from < 0 )
1848 {
1849 from = str.size() + from;
1850 if ( from < 0 )
1851 {
1852 from = 0;
1853 }
1854 }
1855 else if ( from > 0 )
1856 {
1857 //account for the fact that substr() starts at 1
1858 from -= 1;
1859 }
1860
1861 if ( len < 0 )
1862 {
1863 len = str.size() + len - from;
1864 if ( len < 0 )
1865 {
1866 len = 0;
1867 }
1868 }
1869
1870 return QVariant( str.mid( from, len ) );
1871}
1872static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1873{
1874 FEAT_FROM_CONTEXT( context, f )
1875 return QVariant( f.id() );
1876}
1877
1878static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1879{
1880 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1881 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1882 bool foundLayer = false;
1883 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, geom]( QgsMapLayer * mapLayer )
1884 {
1885 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1886 if ( !layer || !layer->dataProvider() )
1887 {
1888 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1889 return QVariant();
1890 }
1891
1892 if ( bandNb < 1 || bandNb > layer->bandCount() )
1893 {
1894 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1895 return QVariant();
1896 }
1897
1898 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1899 {
1900 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1901 return QVariant();
1902 }
1903
1904 QgsPointXY point = geom.asPoint();
1905 if ( geom.isMultipart() )
1906 {
1907 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1908 if ( multiPoint.count() == 1 )
1909 {
1910 point = multiPoint[0];
1911 }
1912 else
1913 {
1914 // if the geometry contains more than one part, return an undefined value
1915 return QVariant();
1916 }
1917 }
1918
1919 double value = layer->dataProvider()->sample( point, bandNb );
1920 return std::isnan( value ) ? QVariant() : value;
1921 },
1922 foundLayer );
1923
1924 if ( !foundLayer )
1925 {
1926 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1927 return QVariant();
1928 }
1929 else
1930 {
1931 return res;
1932 }
1933}
1934
1935static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1936{
1937 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1938 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1939
1940 bool foundLayer = false;
1941 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, value]( QgsMapLayer * mapLayer )-> QVariant
1942 {
1943 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1944 if ( !layer || !layer->dataProvider() )
1945 {
1946 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1947 return QVariant();
1948 }
1949
1950 if ( bandNb < 1 || bandNb > layer->bandCount() )
1951 {
1952 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1953 return QVariant();
1954 }
1955
1956 if ( std::isnan( value ) )
1957 {
1958 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1959 return QVariant();
1960 }
1961
1962 if ( ! layer->dataProvider()->attributeTable( bandNb ) )
1963 {
1964 return QVariant();
1965 }
1966
1967 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1968 if ( data.isEmpty() )
1969 {
1970 return QVariant();
1971 }
1972
1973 QVariantMap result;
1974 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1975 for ( int idx = 0; idx < static_cast<int>( fields.count( ) ) && idx < static_cast<int>( data.count() ); ++idx )
1976 {
1977 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1978 if ( field.isColor() || field.isRamp() )
1979 {
1980 continue;
1981 }
1982 result.insert( fields.at( idx ).name, data.at( idx ) );
1983 }
1984
1985 return result;
1986 }, foundLayer );
1987
1988 if ( !foundLayer )
1989 {
1990 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1991 return QVariant();
1992 }
1993 else
1994 {
1995 return res;
1996 }
1997}
1998
1999static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2000{
2001 if ( !context )
2002 return QVariant();
2003
2004 return context->feature();
2005}
2006
2007static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2008{
2009 QgsFeature feature;
2010 QString attr;
2011 if ( values.size() == 1 )
2012 {
2013 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2014 feature = context->feature();
2015 }
2016 else if ( values.size() == 2 )
2017 {
2018 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2019 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2020 }
2021 else
2022 {
2023 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
2024 return QVariant();
2025 }
2026
2027 return feature.attribute( attr );
2028}
2029
2030static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2031{
2032 QString table { R"html(
2033 <table>
2034 <thead>
2035 <tr><th>%1</th></tr>
2036 </thead>
2037 <tbody>
2038 <tr><td>%2</td></tr>
2039 </tbody>
2040 </table>)html" };
2041 QVariantMap dict;
2042 if ( values.size() == 1 )
2043 {
2044 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2045 }
2046 else
2047 {
2048 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
2049 return QVariant();
2050 }
2051
2052 if ( dict.isEmpty() )
2053 {
2054 return QVariant();
2055 }
2056
2057 QStringList headers;
2058 QStringList cells;
2059
2060 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2061 {
2062 headers.push_back( it.key().toHtmlEscaped() );
2063 cells.push_back( it.value().toString( ).toHtmlEscaped() );
2064 }
2065
2066 return table.arg( headers.join( "</th><th>"_L1 ), cells.join( "</td><td>"_L1 ) );
2067}
2068
2069static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2070{
2071 QString table { R"html(
2072 <dl>
2073 %1
2074 </dl>)html" };
2075 QVariantMap dict;
2076 if ( values.size() == 1 )
2077 {
2078 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2079 }
2080 else
2081 {
2082 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
2083 return QVariant();
2084 }
2085
2086 if ( dict.isEmpty() )
2087 {
2088 return QVariant();
2089 }
2090
2091 QString rows;
2092
2093 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2094 {
2095 rows.append( u"<dt>%1</dt><dd>%2</dd>"_s.arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2096 }
2097
2098 return table.arg( rows );
2099}
2100
2101static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2102{
2103 QVariant layer;
2104 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2105 {
2106 layer = context->variable( u"layer"_s );
2107 }
2108 else
2109 {
2110 //first node is layer id or name
2111 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2113 layer = node->eval( parent, context );
2115 }
2116
2117 QgsFeature feature;
2118 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2119 {
2120 feature = context->feature();
2121 }
2122 else
2123 {
2124 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2125 }
2126
2128 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2129 if ( strength == "hard"_L1 )
2130 {
2132 }
2133 else if ( strength == "soft"_L1 )
2134 {
2136 }
2137
2138 bool foundLayer = false;
2139 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2140 {
2141 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2142 if ( !layer )
2143 {
2144 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2145 return QVariant();
2146 }
2147
2148 const QgsFields fields = layer->fields();
2149 bool valid = true;
2150 for ( int i = 0; i < fields.size(); i++ )
2151 {
2152 QStringList errors;
2153 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2154 if ( !valid )
2155 {
2156 break;
2157 }
2158 }
2159
2160 return valid;
2161 }, foundLayer );
2162
2163 if ( !foundLayer )
2164 {
2165 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2166 return QVariant();
2167 }
2168
2169 return res;
2170}
2171
2172static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2173{
2174 QVariant layer;
2175 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2176 {
2177 layer = context->variable( u"layer"_s );
2178 }
2179 else
2180 {
2181 //first node is layer id or name
2182 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2184 layer = node->eval( parent, context );
2186 }
2187
2188 QgsFeature feature;
2189 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2190 {
2191 feature = context->feature();
2192 }
2193 else
2194 {
2195 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2196 }
2197
2199 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2200 if ( strength == "hard"_L1 )
2201 {
2203 }
2204 else if ( strength == "soft"_L1 )
2205 {
2207 }
2208
2209 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2210
2211 bool foundLayer = false;
2212 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, attributeName, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2213 {
2214 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2215 if ( !layer )
2216 {
2217 return QVariant();
2218 }
2219
2220 const int fieldIndex = layer->fields().indexFromName( attributeName );
2221 if ( fieldIndex == -1 )
2222 {
2223 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2224 return QVariant();
2225 }
2226
2227 QStringList errors;
2228 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2229 return valid;
2230 }, foundLayer );
2231
2232 if ( !foundLayer )
2233 {
2234 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2235 return QVariant();
2236 }
2237
2238 return res;
2239}
2240
2241static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2242{
2243 QgsFeature feature;
2244 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2245 {
2246 feature = context->feature();
2247 }
2248 else
2249 {
2250 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2251 }
2252
2253 const QgsFields fields = feature.fields();
2254 QVariantMap result;
2255 for ( int i = 0; i < fields.count(); ++i )
2256 {
2257 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2258 }
2259 return result;
2260}
2261
2262static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2263{
2264 QgsVectorLayer *layer = nullptr;
2265 QgsFeature feature;
2266
2267 // TODO this expression function is NOT thread safe
2269 if ( values.isEmpty() )
2270 {
2271 feature = context->feature();
2272 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2273 }
2274 else if ( values.size() == 1 )
2275 {
2276 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2277 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2278 }
2279 else if ( values.size() == 2 )
2280 {
2281 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2282 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2283 }
2284 else
2285 {
2286 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2287 return QVariant();
2288 }
2290
2291 if ( !layer )
2292 {
2293 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2294 return QVariant();
2295 }
2296
2297 if ( !feature.isValid() )
2298 {
2299 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2300 return QVariant();
2301 }
2302
2303 const QgsFields fields = feature.fields();
2304 QVariantMap result;
2305 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2306 {
2307 const QString fieldName { fields.at( fieldIndex ).name() };
2308 const QVariant attributeVal = feature.attribute( fieldIndex );
2309 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer->id(), fieldName, attributeVal.toString() );
2310 if ( context && context->hasCachedValue( cacheValueKey ) )
2311 {
2312 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2313 }
2314 else
2315 {
2316 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2318 QVariant cache;
2319 if ( context )
2320 {
2321 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer->id(), fieldName );
2322
2323 if ( !context->hasCachedValue( cacheKey ) )
2324 {
2325 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2326 context->setCachedValue( cacheKey, cache );
2327 }
2328 else
2329 {
2330 cache = context->cachedValue( cacheKey );
2331 }
2332 }
2333 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2334
2335 result.insert( fields.at( fieldIndex ).name(), value );
2336
2337 if ( context )
2338 {
2339 context->setCachedValue( cacheValueKey, value );
2340 }
2341
2342 }
2343 }
2344 return result;
2345}
2346
2347static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2348{
2349 QgsVectorLayer *layer = nullptr;
2350 QgsFeature feature;
2351 bool evaluate = true;
2352
2353 // TODO this expression function is NOT thread safe
2355 if ( values.isEmpty() )
2356 {
2357 feature = context->feature();
2358 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2359 }
2360 else if ( values.size() == 1 )
2361 {
2362 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2363 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2364 }
2365 else if ( values.size() == 2 )
2366 {
2367 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2368 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2369 }
2370 else if ( values.size() == 3 )
2371 {
2372 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2373 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2374 evaluate = values.value( 2 ).toBool();
2375 }
2376 else
2377 {
2378 if ( isMaptip )
2379 {
2380 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2381 }
2382 else
2383 {
2384 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2385 }
2386 return QVariant();
2387 }
2388
2389 if ( !layer )
2390 {
2391 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2392 return QVariant( );
2393 }
2395
2396 if ( !feature.isValid() )
2397 {
2398 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2399 return QVariant( );
2400 }
2401
2402 if ( ! evaluate )
2403 {
2404 if ( isMaptip )
2405 {
2406 return layer->mapTipTemplate();
2407 }
2408 else
2409 {
2410 return layer->displayExpression();
2411 }
2412 }
2413
2414 QgsExpressionContext subContext( *context );
2415 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2416 subContext.setFeature( feature );
2417
2418 if ( isMaptip )
2419 {
2420 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2421 }
2422 else
2423 {
2424 QgsExpression exp( layer->displayExpression() );
2425 exp.prepare( &subContext );
2426 return exp.evaluate( &subContext ).toString();
2427 }
2428}
2429
2430static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2431{
2432 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2433}
2434
2435static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2436{
2437 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2438}
2439
2440static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2441{
2442 QgsFeature feature;
2443 QVariant layer;
2444 if ( values.isEmpty() )
2445 {
2446 feature = context->feature();
2447 layer = context->variable( u"layer"_s );
2448 }
2449 else if ( values.size() == 1 )
2450 {
2451 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2452 layer = context->variable( u"layer"_s );
2453 }
2454 else if ( values.size() == 2 )
2455 {
2456 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2457 layer = values.at( 0 );
2458 }
2459 else
2460 {
2461 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2462 return QVariant();
2463 }
2464
2465 bool foundLayer = false;
2466 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [feature]( QgsMapLayer * mapLayer ) -> QVariant
2467 {
2468 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2469 if ( !layer || !feature.isValid() )
2470 {
2471 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2472 }
2473
2474 return layer->selectedFeatureIds().contains( feature.id() );
2475 }, foundLayer );
2476 if ( !foundLayer )
2477 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2478 else
2479 return res;
2480}
2481
2482static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2483{
2484 QVariant layer;
2485
2486 if ( values.isEmpty() )
2487 layer = context->variable( u"layer"_s );
2488 else if ( values.count() == 1 )
2489 layer = values.at( 0 );
2490 else
2491 {
2492 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2493 return QVariant();
2494 }
2495
2496 bool foundLayer = false;
2497 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, []( QgsMapLayer * mapLayer ) -> QVariant
2498 {
2499 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2500 if ( !layer )
2501 {
2502 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2503 }
2504
2505 return layer->selectedFeatureCount();
2506 }, foundLayer );
2507 if ( !foundLayer )
2508 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2509 else
2510 return res;
2511}
2512
2513static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2514{
2515 static QMap<QString, qlonglong> counterCache;
2516 QVariant functionResult;
2517
2518 auto fetchAndIncrementFunc = [ values, parent, &functionResult ]( QgsMapLayer * mapLayer, const QString & databaseArgument )
2519 {
2520 QString database;
2521
2522 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2523
2524 if ( layer )
2525 {
2526 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2527 database = decodedUri.value( u"path"_s ).toString();
2528 if ( database.isEmpty() )
2529 {
2530 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2531 }
2532 }
2533 else
2534 {
2535 database = databaseArgument;
2536 }
2537
2538 const QString table = values.at( 1 ).toString();
2539 const QString idColumn = values.at( 2 ).toString();
2540 const QString filterAttribute = values.at( 3 ).toString();
2541 const QVariant filterValue = values.at( 4 ).toString();
2542 const QVariantMap defaultValues = values.at( 5 ).toMap();
2543
2544 // read from database
2546 sqlite3_statement_unique_ptr sqliteStatement;
2547
2548 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2549 {
2550 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2551 functionResult = QVariant();
2552 return;
2553 }
2554
2555 QString errorMessage;
2556 QString currentValSql;
2557
2558 qlonglong nextId = 0;
2559 bool cachedMode = false;
2560 bool valueRetrieved = false;
2561
2562 QString cacheString = u"%1:%2:%3:%4:%5"_s.arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2563
2564 // Running in transaction mode, check for cached value first
2565 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2566 {
2567 cachedMode = true;
2568
2569 auto cachedCounter = counterCache.find( cacheString );
2570
2571 if ( cachedCounter != counterCache.end() )
2572 {
2573 qlonglong &cachedValue = cachedCounter.value();
2574 nextId = cachedValue;
2575 nextId += 1;
2576 cachedValue = nextId;
2577 valueRetrieved = true;
2578 }
2579 }
2580
2581 // Either not in cached mode or no cached value found, obtain from DB
2582 if ( !cachedMode || !valueRetrieved )
2583 {
2584 int result = SQLITE_ERROR;
2585
2586 currentValSql = u"SELECT %1 FROM %2"_s.arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2587 if ( !filterAttribute.isNull() )
2588 {
2589 currentValSql += u" WHERE %1 = %2"_s.arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2590 }
2591
2592 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2593
2594 if ( result == SQLITE_OK )
2595 {
2596 nextId = 0;
2597 if ( sqliteStatement.step() == SQLITE_ROW )
2598 {
2599 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2600 }
2601
2602 // If in cached mode: add value to cache and connect to transaction
2603 if ( cachedMode && result == SQLITE_OK )
2604 {
2605 counterCache.insert( cacheString, nextId );
2606
2607 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2608 {
2609 counterCache.remove( cacheString );
2610 } );
2611 }
2612 valueRetrieved = true;
2613 }
2614 }
2615
2616 if ( valueRetrieved )
2617 {
2618 QString upsertSql;
2619 upsertSql = u"INSERT OR REPLACE INTO %1"_s.arg( QgsSqliteUtils::quotedIdentifier( table ) );
2620 QStringList cols;
2621 QStringList vals;
2622 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2623 vals << QgsSqliteUtils::quotedValue( nextId );
2624
2625 if ( !filterAttribute.isNull() )
2626 {
2627 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2628 vals << QgsSqliteUtils::quotedValue( filterValue );
2629 }
2630
2631 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2632 {
2633 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2634 vals << iter.value().toString();
2635 }
2636
2637 upsertSql += " ("_L1 + cols.join( ',' ) + ')';
2638 upsertSql += " VALUES "_L1;
2639 upsertSql += '(' + vals.join( ',' ) + ')';
2640
2641 int result = SQLITE_ERROR;
2642 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2643 {
2644 QgsTransaction *transaction = layer->dataProvider()->transaction();
2645 if ( transaction->executeSql( upsertSql, errorMessage ) )
2646 {
2647 result = SQLITE_OK;
2648 }
2649 }
2650 else
2651 {
2652 result = sqliteDb.exec( upsertSql, errorMessage );
2653 }
2654 if ( result == SQLITE_OK )
2655 {
2656 functionResult = QVariant( nextId );
2657 return;
2658 }
2659 else
2660 {
2661 parent->setEvalErrorString( u"Could not increment value: SQLite error: \"%1\" (%2)."_s.arg( errorMessage, QString::number( result ) ) );
2662 functionResult = QVariant();
2663 return;
2664 }
2665 }
2666
2667 functionResult = QVariant();
2668 };
2669
2670 bool foundLayer = false;
2671 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer * layer )
2672 {
2673 fetchAndIncrementFunc( layer, QString() );
2674 }, foundLayer );
2675 if ( !foundLayer )
2676 {
2677 const QString databasePath = values.at( 0 ).toString();
2678 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath]
2679 {
2680 fetchAndIncrementFunc( nullptr, databasePath );
2681 } );
2682 }
2683
2684 return functionResult;
2685}
2686
2687static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2688{
2689 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2690 if ( !crs.isValid() )
2691 return QVariant();
2692 return crs.authid();
2693}
2694
2695static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2696{
2697 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2698 QgsCoordinateReferenceSystem crs( definition );
2699
2700 if ( !crs.isValid() )
2701 {
2702 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to coordinate reference system" ).arg( definition ) );
2703 }
2704
2705 return QVariant::fromValue( crs );
2706}
2707
2708static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2709{
2710 QString concat;
2711 for ( const QVariant &value : values )
2712 {
2713 if ( !QgsVariantUtils::isNull( value ) )
2714 concat += QgsExpressionUtils::getStringValue( value, parent );
2715 }
2716 return concat;
2717}
2718
2719static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2720{
2721 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2722 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2723}
2724
2725static QVariant fcnUnaccent(
2726 const QVariantList &values,
2727 const QgsExpressionContext *context,
2728 QgsExpression *,
2729 const QgsExpressionNodeFunction *node
2730)
2731{
2732 Q_UNUSED( context )
2733 Q_UNUSED( node )
2734
2735 if ( values.isEmpty() || values[0].isNull() )
2736 return QVariant();
2737
2738 return QgsStringUtils::unaccent( values[0].toString() );
2739}
2740
2741
2742static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2743{
2744 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2745 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2746 return string.right( pos );
2747}
2748
2749static QVariant fcnSubstrCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2750{
2751 if ( values.length() < 2 || values.length() > 3 )
2752 return QVariant();
2753
2754 const QString input = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2755 const QString substring = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2756
2757 bool overlapping = false;
2758 if ( values.length() == 3 )
2759 {
2760 overlapping = values.at( 2 ).toBool();
2761 }
2762
2763 if ( substring.isEmpty() )
2764 return QVariant( 0 );
2765
2766 int count = 0;
2767 if ( overlapping )
2768 {
2769 count = input.count( substring );
2770 }
2771 else
2772 {
2773 int pos = 0;
2774 while ( ( pos = input.indexOf( substring, pos ) ) != -1 )
2775 {
2776 count++;
2777 pos += substring.length();
2778 }
2779 }
2780
2781 return QVariant( count );
2782}
2783
2784static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2785{
2786 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2787 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2788 return string.left( pos );
2789}
2790
2791static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2792{
2793 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2794 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2795 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2796 return string.leftJustified( length, fill.at( 0 ), true );
2797}
2798
2799static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2800{
2801 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2802 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2803 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2804 return string.rightJustified( length, fill.at( 0 ), true );
2805}
2806
2807static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2808{
2809 if ( values.size() < 1 )
2810 {
2811 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2812 return QVariant();
2813 }
2814
2815 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2816 for ( int n = 1; n < values.length(); n++ )
2817 {
2818 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2819 }
2820 return string;
2821}
2822
2823
2824static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2825{
2826 return QVariant( QDateTime::currentDateTime() );
2827}
2828
2829static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2830{
2831 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2832 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2833 if ( format.isEmpty() && !language.isEmpty() )
2834 {
2835 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2836 return QVariant( QDate() );
2837 }
2838
2839 if ( format.isEmpty() && language.isEmpty() )
2840 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2841
2842 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2843 QLocale locale = QLocale();
2844 if ( !language.isEmpty() )
2845 {
2846 locale = QLocale( language );
2847 }
2848
2849 QDate date = locale.toDate( datestring, format );
2850 if ( !date.isValid() )
2851 {
2852 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2853 date = QDate();
2854 }
2855 return QVariant( date );
2856}
2857
2858static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2859{
2860 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2861 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2862 if ( format.isEmpty() && !language.isEmpty() )
2863 {
2864 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2865 return QVariant( QTime() );
2866 }
2867
2868 if ( format.isEmpty() && language.isEmpty() )
2869 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2870
2871 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2872 QLocale locale = QLocale();
2873 if ( !language.isEmpty() )
2874 {
2875 locale = QLocale( language );
2876 }
2877
2878 QTime time = locale.toTime( timestring, format );
2879 if ( !time.isValid() )
2880 {
2881 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2882 time = QTime();
2883 }
2884 return QVariant( time );
2885}
2886
2887static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2888{
2889 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2890}
2891
2892/*
2893 * DMS functions
2894 */
2895
2896static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2897{
2898 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2899 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2900 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2901
2902 QString formatString;
2903 if ( values.count() > 3 )
2904 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2905
2907 if ( formatString.compare( "suffix"_L1, Qt::CaseInsensitive ) == 0 )
2908 {
2910 }
2911 else if ( formatString.compare( "aligned"_L1, Qt::CaseInsensitive ) == 0 )
2912 {
2914 }
2915 else if ( ! formatString.isEmpty() )
2916 {
2917 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2918 return QVariant();
2919 }
2920
2921 if ( axis.compare( 'x'_L1, Qt::CaseInsensitive ) == 0 )
2922 {
2923 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2924 }
2925 else if ( axis.compare( 'y'_L1, Qt::CaseInsensitive ) == 0 )
2926 {
2927 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2928 }
2929 else
2930 {
2931 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2932 return QVariant();
2933 }
2934}
2935
2936static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2937{
2939 return floatToDegreeFormat( format, values, context, parent, node );
2940}
2941
2942static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2943{
2944 double value = 0.0;
2945 bool ok = false;
2946 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2947
2948 return ok ? QVariant( value ) : QVariant();
2949}
2950
2951static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2952{
2954 return floatToDegreeFormat( format, values, context, parent, node );
2955}
2956
2957static QVariant fcnExtractDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2958{
2959 const double decimalDegrees = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2960 return static_cast< int >( decimalDegrees );
2961}
2962
2963static QVariant fcnExtractMinutes( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2964{
2965 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
2966 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
2967 return static_cast< int >( remainder * 60 );
2968}
2969
2970static QVariant fcnExtractSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2971{
2972 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
2973 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
2974 const double remainderInMinutes = remainder * 60;
2975 const double remainderSecondsFraction = remainderInMinutes - static_cast< int >( remainderInMinutes );
2976 // do not truncate to int, this function returns decimal seconds!
2977 return remainderSecondsFraction * 60;
2978}
2979
2980static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2981{
2982 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2983 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2984 qint64 seconds = d2.secsTo( d1 );
2985 return QVariant::fromValue( QgsInterval( seconds ) );
2986}
2987
2988static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2989{
2990 if ( !values.at( 0 ).canConvert<QDate>() )
2991 return QVariant();
2992
2993 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2994 if ( !date.isValid() )
2995 return QVariant();
2996
2997 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2998 // (to match PostgreSQL behavior)
2999 return date.dayOfWeek() % 7;
3000}
3001
3002static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3003{
3004 QVariant value = values.at( 0 );
3005 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3006 if ( inter.isValid() )
3007 {
3008 return QVariant( inter.days() );
3009 }
3010 else
3011 {
3012 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3013 return QVariant( d1.date().day() );
3014 }
3015}
3016
3017static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3018{
3019 QVariant value = values.at( 0 );
3020 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3021 if ( inter.isValid() )
3022 {
3023 return QVariant( inter.years() );
3024 }
3025 else
3026 {
3027 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3028 return QVariant( d1.date().year() );
3029 }
3030}
3031
3032static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3033{
3034 QVariant value = values.at( 0 );
3035 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3036 if ( inter.isValid() )
3037 {
3038 return QVariant( inter.months() );
3039 }
3040 else
3041 {
3042 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3043 return QVariant( d1.date().month() );
3044 }
3045}
3046
3047static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3048{
3049 QVariant value = values.at( 0 );
3050 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3051 if ( inter.isValid() )
3052 {
3053 return QVariant( inter.weeks() );
3054 }
3055 else
3056 {
3057 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3058 return QVariant( d1.date().weekNumber() );
3059 }
3060}
3061
3062static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3063{
3064 QVariant value = values.at( 0 );
3065 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3066 if ( inter.isValid() )
3067 {
3068 return QVariant( inter.hours() );
3069 }
3070 else
3071 {
3072 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3073 return QVariant( t1.hour() );
3074 }
3075}
3076
3077static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3078{
3079 QVariant value = values.at( 0 );
3080 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3081 if ( inter.isValid() )
3082 {
3083 return QVariant( inter.minutes() );
3084 }
3085 else
3086 {
3087 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3088 return QVariant( t1.minute() );
3089 }
3090}
3091
3092static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3093{
3094 QVariant value = values.at( 0 );
3095 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3096 if ( inter.isValid() )
3097 {
3098 return QVariant( inter.seconds() );
3099 }
3100 else
3101 {
3102 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3103 return QVariant( t1.second() );
3104 }
3105}
3106
3107static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3108{
3109 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3110 if ( dt.isValid() )
3111 {
3112 return QVariant( dt.toMSecsSinceEpoch() );
3113 }
3114 else
3115 {
3116 return QVariant();
3117 }
3118}
3119
3120static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3121{
3122 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
3123 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
3124 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
3125}
3126
3127static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3128{
3129 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3130 if ( parent->hasEvalError() )
3131 {
3132 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif"_L1 ) );
3133 return QVariant();
3134 }
3135 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3136 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
3137}
3138
3139static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3141 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3142 if ( parent->hasEvalError() )
3143 {
3144 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif_geotag"_L1 ) );
3145 return QVariant();
3146 }
3147 bool ok;
3148 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
3149}
3150
3151double qDateTimeToDecimalYear( const QDateTime &dateTime )
3152{
3153 if ( !dateTime.isValid() )
3154 {
3155 return 0.0;
3156 }
3157
3158 const int year = dateTime.date().year();
3159 const QDateTime startOfYear( QDate( year, 1, 1 ), QTime( 0, 0, 0 ) );
3160 const QDateTime startOfNextYear( QDate( year + 1, 1, 1 ), QTime( 0, 0, 0 ) );
3161 const qint64 secondsFromStartOfYear = startOfYear.secsTo( dateTime );
3162 const qint64 totalSecondsInYear = startOfYear.secsTo( startOfNextYear );
3163 return static_cast<double>( year ) + ( static_cast<double>( secondsFromStartOfYear ) / static_cast< double >( totalSecondsInYear ) );
3164}
3165
3166static QVariant fcnMagneticDeclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3167{
3168 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3169 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3170 if ( parent->hasEvalError() )
3171 {
3172 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination"_L1 ) );
3173 return QVariant();
3174 }
3175 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3176 if ( parent->hasEvalError() )
3177 {
3178 return QVariant();
3179 }
3180 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3181 if ( parent->hasEvalError() )
3182 {
3183 return QVariant();
3184 }
3185 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3186 if ( parent->hasEvalError() )
3187 {
3188 return QVariant();
3189 }
3190 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3191
3192 const QgsMagneticModel model( name, filePath );
3193 try
3194 {
3195 double declination = 0;
3196 if ( model.declination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, declination ) )
3197 {
3198 return declination;
3199 }
3200 else
3201 {
3202 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( model.error() ) );
3203 }
3204 }
3205 catch ( QgsNotSupportedException &e )
3206 {
3207 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( e.what() ) );
3208 }
3209 return QVariant();
3210}
3211
3212static QVariant fcnMagneticInclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3213{
3214 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3215 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3216 if ( parent->hasEvalError() )
3217 {
3218 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination"_L1 ) );
3219 return QVariant();
3220 }
3221 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3222 if ( parent->hasEvalError() )
3223 {
3224 return QVariant();
3225 }
3226 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3227 if ( parent->hasEvalError() )
3228 {
3229 return QVariant();
3230 }
3231 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3232 if ( parent->hasEvalError() )
3233 {
3234 return QVariant();
3235 }
3236 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3237
3238 const QgsMagneticModel model( name, filePath );
3239 try
3240 {
3241 double inclination = 0;
3242 if ( model.inclination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, inclination ) )
3243 {
3244 return inclination;
3245 }
3246 else
3247 {
3248 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( model.error() ) );
3249 }
3250 }
3251 catch ( QgsNotSupportedException &e )
3252 {
3253 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( e.what() ) );
3254 }
3255 return QVariant();
3256}
3257
3258static QVariant fcnMagneticDeclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3259{
3260 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3261 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3262 if ( parent->hasEvalError() )
3263 {
3264 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination_rate_of_change"_L1 ) );
3265 return QVariant();
3266 }
3267 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3268 if ( parent->hasEvalError() )
3269 {
3270 return QVariant();
3271 }
3272 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3273 if ( parent->hasEvalError() )
3274 {
3275 return QVariant();
3276 }
3277 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3278 if ( parent->hasEvalError() )
3279 {
3280 return QVariant();
3281 }
3282 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3283
3284 const QgsMagneticModel model( name, filePath );
3285 try
3286 {
3287 double declination = 0;
3288 double Bx = 0;
3289 double By = 0;
3290 double Bz = 0;
3291 double Bxt = 0;
3292 double Byt = 0;
3293 double Bzt = 0;
3294
3295 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3296 {
3297 double H = 0;
3298 double F = 0;
3299 double D = 0;
3300 double I = 0;
3301 double Ht = 0;
3302 double Ft = 0;
3303 double Dt = 0;
3304 double It = 0;
3305 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3306 {
3307 return Dt;
3308 }
3309 else
3310 {
3311 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change" ) );
3312 }
3313 return declination;
3314 }
3315 else
3316 {
3317 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( model.error() ) );
3318 }
3319 }
3320 catch ( QgsNotSupportedException &e )
3321 {
3322 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( e.what() ) );
3323 }
3324 return QVariant();
3325}
3326
3327static QVariant fcnMagneticInclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3328{
3329 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3330 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3331 if ( parent->hasEvalError() )
3332 {
3333 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination_rate_of_change"_L1 ) );
3334 return QVariant();
3335 }
3336 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3337 if ( parent->hasEvalError() )
3338 {
3339 return QVariant();
3340 }
3341 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3342 if ( parent->hasEvalError() )
3343 {
3344 return QVariant();
3345 }
3346 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3347 if ( parent->hasEvalError() )
3348 {
3349 return QVariant();
3350 }
3351 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3352
3353 const QgsMagneticModel model( name, filePath );
3354 try
3355 {
3356 double declination = 0;
3357 double Bx = 0;
3358 double By = 0;
3359 double Bz = 0;
3360 double Bxt = 0;
3361 double Byt = 0;
3362 double Bzt = 0;
3363
3364 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3365 {
3366 double H = 0;
3367 double F = 0;
3368 double D = 0;
3369 double I = 0;
3370 double Ht = 0;
3371 double Ft = 0;
3372 double Dt = 0;
3373 double It = 0;
3374 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3375 {
3376 return It;
3377 }
3378 else
3379 {
3380 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change" ) );
3381 }
3382 return declination;
3383 }
3384 else
3386 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( model.error() ) );
3387 }
3388 }
3389 catch ( QgsNotSupportedException &e )
3390 {
3391 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( e.what() ) );
3392 }
3393 return QVariant();
3394}
3395
3396#define ENSURE_GEOM_TYPE(f, g, geomtype) \
3397 if ( !(f).hasGeometry() ) \
3398 return QVariant(); \
3399 QgsGeometry g = (f).geometry(); \
3400 if ( (g).type() != (geomtype) ) \
3401 return QVariant();
3402
3403static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3404{
3405 FEAT_FROM_CONTEXT( context, f )
3407 if ( g.isMultipart() )
3408 {
3409 return g.asMultiPoint().at( 0 ).x();
3410 }
3411 else
3412 {
3413 return g.asPoint().x();
3414 }
3415}
3416
3417static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3418{
3419 FEAT_FROM_CONTEXT( context, f )
3421 if ( g.isMultipart() )
3422 {
3423 return g.asMultiPoint().at( 0 ).y();
3424 }
3425 else
3426 {
3427 return g.asPoint().y();
3428 }
3429}
3430
3431static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3432{
3433 FEAT_FROM_CONTEXT( context, f )
3435
3436 if ( g.isEmpty() )
3437 return QVariant();
3438
3439 const QgsAbstractGeometry *abGeom = g.constGet();
3440
3441 if ( g.isEmpty() || !abGeom->is3D() )
3442 return QVariant();
3443
3444 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3445 {
3446 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3447 if ( point )
3448 return point->z();
3449 }
3450 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3451 {
3452 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3453 {
3454 if ( collection->numGeometries() > 0 )
3455 {
3456 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3457 return point->z();
3458 }
3459 }
3460 }
3461
3462 return QVariant();
3463}
3464
3465static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3466{
3467 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3468 if ( geom.isNull() )
3469 return QVariant();
3470
3471 bool isValid = geom.isGeosValid();
3472
3473 return QVariant( isValid );
3474}
3475
3476static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3477{
3478 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3479 if ( geom.isNull() )
3480 return QVariant();
3481
3482 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3483#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
3485#else
3487#endif
3488 if ( methodString.compare( "linework"_L1, Qt::CaseInsensitive ) == 0 )
3490 else if ( methodString.compare( "structure"_L1, Qt::CaseInsensitive ) == 0 )
3492
3493 const bool keepCollapsed = values.value( 2 ).toBool();
3494
3495 QgsGeometry valid;
3496 try
3497 {
3498 valid = geom.makeValid( method, keepCollapsed );
3499 }
3500 catch ( QgsNotSupportedException & )
3501 {
3502 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3503 return QVariant();
3504 }
3505
3506 return QVariant::fromValue( valid );
3507}
3508
3509static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3510{
3511 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3512 if ( geom.isNull() )
3513 return QVariant();
3514
3515 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3516 QVariantList array;
3517 for ( int i = 0; i < multiGeom.size(); ++i )
3518 {
3519 array += QVariant::fromValue( multiGeom.at( i ) );
3520 }
3521
3522 return array;
3523}
3524
3525static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3526{
3527 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3528 if ( geom.isNull() )
3529 return QVariant();
3530
3531 //if single point, return the point's x coordinate
3532 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3533 {
3534 return geom.asPoint().x();
3535 }
3536
3537 //otherwise return centroid x
3538 QgsGeometry centroid = geom.centroid();
3539 QVariant result( centroid.asPoint().x() );
3540 return result;
3541}
3542
3543static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3544{
3545 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3546 if ( geom.isNull() )
3547 return QVariant();
3548
3549 //if single point, return the point's y coordinate
3550 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3551 {
3552 return geom.asPoint().y();
3553 }
3554
3555 //otherwise return centroid y
3556 QgsGeometry centroid = geom.centroid();
3557 QVariant result( centroid.asPoint().y() );
3558 return result;
3559}
3560
3561static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3562{
3563 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3564 if ( geom.isNull() )
3565 return QVariant(); //or 0?
3566
3567 if ( !geom.constGet()->is3D() )
3568 return QVariant();
3569
3570 //if single point, return the point's z coordinate
3571 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3572 {
3574 if ( point )
3575 return point->z();
3576 }
3577 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3578 {
3580 {
3581 if ( collection->numGeometries() == 1 )
3582 {
3583 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3584 return point->z();
3585 }
3586 }
3587 }
3588
3589 return QVariant();
3590}
3591
3592static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3593{
3594 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3595 if ( geom.isNull() )
3596 return QVariant(); //or 0?
3597
3598 if ( !geom.constGet()->isMeasure() )
3599 return QVariant();
3600
3601 //if single point, return the point's m value
3602 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3603 {
3605 if ( point )
3606 return point->m();
3607 }
3608 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3609 {
3611 {
3612 if ( collection->numGeometries() == 1 )
3613 {
3614 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3615 return point->m();
3616 }
3617 }
3618 }
3619
3620 return QVariant();
3621}
3622
3623static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3624{
3625 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3626
3627 if ( geom.isNull() )
3628 return QVariant();
3629
3630 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3631
3632 if ( idx < 0 )
3633 {
3634 //negative idx
3635 int count = geom.constGet()->nCoordinates();
3636 idx = count + idx;
3637 }
3638 else
3639 {
3640 //positive idx is 1 based
3641 idx -= 1;
3642 }
3643
3644 QgsVertexId vId;
3645 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3646 {
3647 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3648 return QVariant();
3649 }
3650
3651 QgsPoint point = geom.constGet()->vertexAt( vId );
3652 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3653}
3654
3655static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3656{
3657 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3658
3659 if ( geom.isNull() )
3660 return QVariant();
3661
3662 QgsVertexId vId;
3663 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3664 {
3665 return QVariant();
3666 }
3667
3668 QgsPoint point = geom.constGet()->vertexAt( vId );
3669 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3670}
3671
3672static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3673{
3674 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3675
3676 if ( geom.isNull() )
3677 return QVariant();
3678
3679 QgsVertexId vId;
3680 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3681 {
3682 return QVariant();
3683 }
3684
3685 QgsPoint point = geom.constGet()->vertexAt( vId );
3686 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3687}
3688
3689static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3690{
3691 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3692
3693 if ( geom.isNull() )
3694 return QVariant();
3695
3696 bool ignoreClosing = false;
3697 if ( values.length() > 1 )
3698 {
3699 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3700 }
3701
3702 QgsMultiPoint *mp = new QgsMultiPoint();
3703
3704 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3705 for ( const QgsRingSequence &part : sequence )
3706 {
3707 for ( const QgsPointSequence &ring : part )
3708 {
3709 bool skipLast = false;
3710 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3711 {
3712 skipLast = true;
3713 }
3714
3715 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
3716 {
3717 mp->addGeometry( ring.at( i ).clone() );
3718 }
3719 }
3720 }
3721
3722 return QVariant::fromValue( QgsGeometry( mp ) );
3723}
3724
3725static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3726{
3727 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3728
3729 if ( geom.isNull() )
3730 return QVariant();
3731
3732 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3733
3734 //OK, now we have a complete list of segmentized lines from the geometry
3736 for ( QgsLineString *line : linesToProcess )
3737 {
3738 for ( int i = 0; i < line->numPoints() - 1; ++i )
3739 {
3741 segment->setPoints( QgsPointSequence()
3742 << line->pointN( i )
3743 << line->pointN( i + 1 ) );
3744 ml->addGeometry( segment );
3745 }
3746 delete line;
3747 }
3748
3749 return QVariant::fromValue( QgsGeometry( ml ) );
3750}
3751
3752static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3753{
3754 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3755
3756 if ( geom.isNull() )
3757 return QVariant();
3758
3760 if ( !curvePolygon && geom.isMultipart() )
3761 {
3763 {
3764 if ( collection->numGeometries() == 1 )
3765 {
3766 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3767 }
3768 }
3769 }
3770
3771 if ( !curvePolygon )
3772 return QVariant();
3773
3774 //idx is 1 based
3775 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3776
3777 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3778 return QVariant();
3779
3780 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3781 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3782 return result;
3783}
3784
3785static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3786{
3787 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3788
3789 if ( geom.isNull() )
3790 return QVariant();
3791
3793 if ( !collection )
3794 return QVariant();
3795
3796 //idx is 1 based
3797 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3798
3799 if ( idx < 0 || idx >= collection->numGeometries() )
3800 return QVariant();
3801
3802 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3803 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3804 return result;
3805}
3806
3807static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3808{
3809 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3810
3811 if ( geom.isNull() )
3812 return QVariant();
3813
3814 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3815 if ( !boundary )
3816 return QVariant();
3817
3818 return QVariant::fromValue( QgsGeometry( boundary ) );
3819}
3820
3821static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3822{
3823 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3824
3825 if ( geom.isNull() )
3826 return QVariant();
3827
3828 QgsGeometry merged = geom.mergeLines();
3829 if ( merged.isNull() )
3830 return QVariant();
3831
3832 return QVariant::fromValue( merged );
3833}
3834
3835static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3836{
3837 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3838 if ( geom.isNull() )
3839 return QVariant();
3840
3841 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3842 if ( geom2.isNull() )
3843 return QVariant();
3844
3845 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3846 if ( sharedPaths.isNull() )
3847 return QVariant();
3848
3849 return QVariant::fromValue( sharedPaths );
3850}
3851
3852
3853static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3854{
3855 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3856
3857 if ( geom.isNull() )
3858 return QVariant();
3859
3860 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3861
3862 QgsGeometry simplified = geom.simplify( tolerance );
3863 if ( simplified.isNull() )
3864 return QVariant();
3865
3866 return simplified;
3867}
3868
3869static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3870{
3871 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3872
3873 if ( geom.isNull() )
3874 return QVariant();
3875
3876 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3877
3879
3880 QgsGeometry simplified = simplifier.simplify( geom );
3881 if ( simplified.isNull() )
3882 return QVariant();
3883
3884 return simplified;
3885}
3886
3887static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3888{
3889 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3890
3891 if ( geom.isNull() )
3892 return QVariant();
3893
3894 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3895 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3896 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3897 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3898
3899 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3900 if ( smoothed.isNull() )
3901 return QVariant();
3902
3903 return smoothed;
3904}
3905
3906static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3907{
3908 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3909
3910 if ( geom.isNull() )
3911 return QVariant();
3912
3913 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3914 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3915 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3916
3917 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3918 if ( waved.isNull() )
3919 return QVariant();
3920
3921 return waved;
3922}
3923
3924static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3925{
3926 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3927
3928 if ( geom.isNull() )
3929 return QVariant();
3930
3931 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3932 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3933 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3934 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3935 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3936
3937 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3938 minAmplitude, maxAmplitude, seed );
3939 if ( waved.isNull() )
3940 return QVariant();
3941
3942 return waved;
3943}
3944
3945static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3946{
3947 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3948
3949 if ( geom.isNull() )
3950 return QVariant();
3951
3952 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3953 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3954 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3955
3956 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3957 if ( waved.isNull() )
3958 return QVariant();
3959
3960 return waved;
3961}
3962
3963static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3964{
3965 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3966
3967 if ( geom.isNull() )
3968 return QVariant();
3969
3970 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3971 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3972 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3973 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3974 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3975
3976 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3977 minAmplitude, maxAmplitude, seed );
3978 if ( waved.isNull() )
3979 return QVariant();
3980
3981 return waved;
3982}
3983
3984static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3985{
3986 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3987
3988 if ( geom.isNull() )
3989 return QVariant();
3990
3991 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3992 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3993 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3994
3995 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3996 if ( waved.isNull() )
3997 return QVariant();
3998
3999 return waved;
4000}
4001
4002static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4003{
4004 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4005
4006 if ( geom.isNull() )
4007 return QVariant();
4008
4009 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4010 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4011 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4012 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4013 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4014
4015 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
4016 minAmplitude, maxAmplitude, seed );
4017 if ( waved.isNull() )
4018 return QVariant();
4019
4020 return waved;
4021}
4022
4023static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4024{
4025 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4026
4027 if ( geom.isNull() )
4028 return QVariant();
4029
4030 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
4031 QVector< double > dashPattern;
4032 dashPattern.reserve( pattern.size() );
4033 for ( const QVariant &value : std::as_const( pattern ) )
4034 {
4035 bool ok = false;
4036 double v = value.toDouble( &ok );
4037 if ( ok )
4038 {
4039 dashPattern << v;
4040 }
4041 else
4042 {
4043 parent->setEvalErrorString( u"Dash pattern must be an array of numbers"_s );
4044 return QgsGeometry();
4045 }
4046 }
4047
4048 if ( dashPattern.size() % 2 != 0 )
4049 {
4050 parent->setEvalErrorString( u"Dash pattern must contain an even number of elements"_s );
4051 return QgsGeometry();
4052 }
4053
4054 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
4056 if ( startRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4058 else if ( startRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4060 else if ( startRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4062 else if ( startRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4064 else if ( startRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4066 else
4067 {
4068 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( startRuleString ) );
4069 return QgsGeometry();
4070 }
4071
4072 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4074 if ( endRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4076 else if ( endRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4078 else if ( endRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4080 else if ( endRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4082 else if ( endRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4084 else
4085 {
4086 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( endRuleString ) );
4087 return QgsGeometry();
4088 }
4089
4090 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4092 if ( adjustString.compare( "both"_L1, Qt::CaseInsensitive ) == 0 )
4094 else if ( adjustString.compare( "dash"_L1, Qt::CaseInsensitive ) == 0 )
4096 else if ( adjustString.compare( "gap"_L1, Qt::CaseInsensitive ) == 0 )
4098 else
4099 {
4100 parent->setEvalErrorString( u"'%1' is not a valid dash pattern size adjustment"_s.arg( adjustString ) );
4101 return QgsGeometry();
4102 }
4103
4104 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4105
4106 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
4107 if ( result.isNull() )
4108 return QVariant();
4109
4110 return result;
4111}
4112
4113static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4114{
4115 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4116
4117 if ( geom.isNull() )
4118 return QVariant();
4119
4120 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
4121 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
4122 if ( densified.isNull() )
4123 return QVariant();
4124
4125 return densified;
4126}
4127
4128static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4129{
4130 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4131
4132 if ( geom.isNull() )
4133 return QVariant();
4134
4135 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4136 const QgsGeometry densified = geom.densifyByDistance( distance );
4137 if ( densified.isNull() )
4138 return QVariant();
4139
4140 return densified;
4141}
4142
4143static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4144{
4145 QVariantList list;
4146 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
4147 {
4148 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4149 }
4150 else
4151 {
4152 list = values;
4153 }
4154
4155 QVector< QgsGeometry > parts;
4156 parts.reserve( list.size() );
4157 for ( const QVariant &value : std::as_const( list ) )
4158 {
4159 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
4160 if ( part.isNull() )
4161 return QgsGeometry();
4162 parts << part;
4163 }
4164
4165 return QgsGeometry::collectGeometry( parts );
4166}
4167
4168static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4169{
4170 if ( values.count() < 2 || values.count() > 4 )
4171 {
4172 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
4173 return QVariant();
4174 }
4175
4176 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4177 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4178 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
4179 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
4180 switch ( values.count() )
4181 {
4182 case 2:
4183 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
4184 case 3:
4185 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
4186 case 4:
4187 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
4188 }
4189 return QVariant(); //avoid warning
4190}
4191
4192static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4193{
4194 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4195 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4196 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4197 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
4198}
4199
4200static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4201{
4202 if ( values.empty() )
4203 {
4204 return QVariant();
4205 }
4206
4207 QVector<QgsPoint> points;
4208 points.reserve( values.count() );
4209
4210 auto addPoint = [&points]( const QgsGeometry & geom )
4211 {
4212 if ( geom.isNull() )
4213 return;
4214
4215 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4216 return;
4217
4219 if ( !point )
4220 return;
4221
4222 points << *point;
4223 };
4224
4225 for ( const QVariant &value : values )
4226 {
4227 if ( value.userType() == QMetaType::Type::QVariantList )
4228 {
4229 const QVariantList list = value.toList();
4230 for ( const QVariant &v : list )
4231 {
4232 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
4233 }
4234 }
4235 else
4236 {
4237 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
4238 }
4239 }
4240
4241 if ( points.count() < 2 )
4242 return QVariant();
4243
4244 return QgsGeometry( new QgsLineString( points ) );
4245}
4246
4247static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4248{
4249 if ( values.count() < 1 )
4250 {
4251 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
4252 return QVariant();
4253 }
4254
4255 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4256
4257 if ( outerRing.type() == Qgis::GeometryType::Polygon )
4258 return outerRing; // if it's already a polygon we have nothing to do
4259
4260 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
4261 return QVariant();
4262
4263 auto polygon = std::make_unique< QgsPolygon >();
4264
4265 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
4266 if ( !exteriorRing && outerRing.isMultipart() )
4267 {
4269 {
4270 if ( collection->numGeometries() == 1 )
4271 {
4272 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4273 }
4274 }
4275 }
4276
4277 if ( !exteriorRing )
4278 return QVariant();
4279
4280 polygon->setExteriorRing( exteriorRing->segmentize() );
4281
4282
4283 for ( int i = 1; i < values.count(); ++i )
4284 {
4285 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
4286 if ( ringGeom.isNull() )
4287 continue;
4288
4289 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
4290 continue;
4291
4292 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
4293 if ( !ring && ringGeom.isMultipart() )
4294 {
4296 {
4297 if ( collection->numGeometries() == 1 )
4298 {
4299 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4300 }
4301 }
4302 }
4303
4304 if ( !ring )
4305 continue;
4306
4307 polygon->addInteriorRing( ring->segmentize() );
4308 }
4309
4310 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
4311}
4312
4313static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4314{
4315 auto tr = std::make_unique<QgsTriangle>();
4316 auto lineString = std::make_unique<QgsLineString>();
4317 lineString->clear();
4318
4319 for ( const QVariant &value : values )
4320 {
4321 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
4322 if ( geom.isNull() )
4323 return QVariant();
4324
4325 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4326 return QVariant();
4327
4329 if ( !point && geom.isMultipart() )
4330 {
4332 {
4333 if ( collection->numGeometries() == 1 )
4334 {
4335 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4336 }
4337 }
4338 }
4339
4340 if ( !point )
4341 return QVariant();
4342
4343 lineString->addVertex( *point );
4344 }
4345
4346 tr->setExteriorRing( lineString.release() );
4347
4348 return QVariant::fromValue( QgsGeometry( tr.release() ) );
4349}
4350
4351static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4352{
4353 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4354 if ( geom.isNull() )
4355 return QVariant();
4356
4357 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4358 return QVariant();
4359
4360 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4361 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4362
4363 if ( segment < 3 )
4364 {
4365 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4366 return QVariant();
4367 }
4369 if ( !point && geom.isMultipart() )
4370 {
4372 {
4373 if ( collection->numGeometries() == 1 )
4374 {
4375 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4376 }
4377 }
4378 }
4379 if ( !point )
4380 return QVariant();
4381
4382 QgsCircle circ( *point, radius );
4383 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4384}
4385
4386static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4387{
4388 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4389 if ( geom.isNull() )
4390 return QVariant();
4391
4392 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4393 return QVariant();
4394
4395 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4396 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4397 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4398 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
4399 if ( segment < 3 )
4400 {
4401 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4402 return QVariant();
4403 }
4405 if ( !point && geom.isMultipart() )
4406 {
4408 {
4409 if ( collection->numGeometries() == 1 )
4410 {
4411 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4412 }
4413 }
4414 }
4415 if ( !point )
4416 return QVariant();
4417
4418 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4419 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4420}
4421
4422static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4423{
4424
4425 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4426 if ( pt1.isNull() )
4427 return QVariant();
4428
4429 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4430 return QVariant();
4431
4432 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4433 if ( pt2.isNull() )
4434 return QVariant();
4435
4436 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4437 return QVariant();
4438
4439 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4440 if ( nbEdges < 3 )
4441 {
4442 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4443 return QVariant();
4444 }
4445
4446 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4448 {
4449 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4450 return QVariant();
4451 }
4452
4454 if ( !center && pt1.isMultipart() )
4455 {
4457 {
4458 if ( collection->numGeometries() == 1 )
4459 {
4460 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4461 }
4462 }
4463 }
4464 if ( !center )
4465 return QVariant();
4466
4468 if ( !corner && pt2.isMultipart() )
4469 {
4471 {
4472 if ( collection->numGeometries() == 1 )
4473 {
4474 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4475 }
4476 }
4477 }
4478 if ( !corner )
4479 return QVariant();
4480
4481 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4482
4483 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4484
4485}
4486
4487static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4488{
4489 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4490 if ( pt1.isNull() )
4491 return QVariant();
4492 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4493 return QVariant();
4494
4495 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4496 if ( pt2.isNull() )
4497 return QVariant();
4498 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4499 return QVariant();
4500
4501 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4502 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4503 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4504
4505 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4506}
4507
4508static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4509{
4510 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4511 if ( pt1.isNull() )
4512 return QVariant();
4513 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4514 return QVariant();
4515
4516 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4517 if ( pt2.isNull() )
4518 return QVariant();
4519 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4520 return QVariant();
4521
4522 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4523 if ( pt3.isNull() )
4524 return QVariant();
4525 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4526 return QVariant();
4527
4528 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4529 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4530 {
4531 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4532 return QVariant();
4533 }
4534 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4535 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4536 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4537 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4538 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4539}
4540
4541static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4542{
4543 if ( geom.isNull() )
4544 return QVariant();
4545
4546 if ( idx < 0 )
4547 {
4548 idx += geom.constGet()->nCoordinates();
4549 }
4550 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4551 {
4552 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4553 return QVariant();
4554 }
4555 return QVariant::fromValue( geom.vertexAt( idx ) );
4556}
4557
4558// function used for the old $ style
4559static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4560{
4561 FEAT_FROM_CONTEXT( context, feature )
4562 const QgsGeometry geom = feature.geometry();
4563 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4564
4565 const QVariant v = pointAt( geom, idx, parent );
4566
4567 if ( !v.isNull() )
4568 return QVariant( v.value<QgsPoint>().x() );
4569 else
4570 return QVariant();
4571}
4572static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4573{
4574 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))
4575 {
4576 return fcnOldXat( values, f, parent, node );
4577 }
4578 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)
4579 {
4580 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4581 }
4582
4583 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4584 if ( geom.isNull() )
4585 {
4586 return QVariant();
4587 }
4588
4589 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4590
4591 const QVariant v = pointAt( geom, vertexNumber, parent );
4592 if ( !v.isNull() )
4593 return QVariant( v.value<QgsPoint>().x() );
4594 else
4595 return QVariant();
4596}
4597
4598// function used for the old $ style
4599static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4600{
4601 FEAT_FROM_CONTEXT( context, feature )
4602 const QgsGeometry geom = feature.geometry();
4603 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4604
4605 const QVariant v = pointAt( geom, idx, parent );
4606
4607 if ( !v.isNull() )
4608 return QVariant( v.value<QgsPoint>().y() );
4609 else
4610 return QVariant();
4611}
4612static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4613{
4614 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))
4615 {
4616 return fcnOldYat( values, f, parent, node );
4617 }
4618 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)
4619 {
4620 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4621 }
4622
4623 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4624 if ( geom.isNull() )
4625 {
4626 return QVariant();
4627 }
4628
4629 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4630
4631 const QVariant v = pointAt( geom, vertexNumber, parent );
4632 if ( !v.isNull() )
4633 return QVariant( v.value<QgsPoint>().y() );
4634 else
4635 return QVariant();
4636}
4637
4638static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4639{
4640 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4641 if ( geom.isNull() )
4642 {
4643 return QVariant();
4644 }
4645
4646 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4647
4648 const QVariant v = pointAt( geom, vertexNumber, parent );
4649 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4650 return QVariant( v.value<QgsPoint>().z() );
4651 else
4652 return QVariant();
4653}
4654
4655static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4656{
4657 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4658 if ( geom.isNull() )
4659 {
4660 return QVariant();
4661 }
4662
4663 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4664
4665 const QVariant v = pointAt( geom, vertexNumber, parent );
4666 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4667 return QVariant( v.value<QgsPoint>().m() );
4668 else
4669 return QVariant();
4670}
4671
4672
4673static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4674{
4675 if ( !context )
4676 return QVariant();
4677
4678 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4679 if ( context->hasGeometry() )
4680 return context->geometry();
4681 else
4682 {
4683 FEAT_FROM_CONTEXT( context, f )
4684 QgsGeometry geom = f.geometry();
4685 if ( !geom.isNull() )
4686 return QVariant::fromValue( geom );
4687 else
4688 return QVariant();
4689 }
4690}
4691
4692static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4693{
4694 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4695 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4696 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4697 return result;
4698}
4699
4700static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4701{
4702 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4703 if ( wkb.isNull() )
4704 return QVariant();
4705
4706 QgsGeometry geom;
4707 geom.fromWkb( wkb );
4708 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4709}
4710
4711static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4712{
4713 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4714 QgsOgcUtils::Context ogcContext;
4715 if ( context )
4716 {
4717 QgsWeakMapLayerPointer mapLayerPtr {context->variable( u"layer"_s ).value<QgsWeakMapLayerPointer>() };
4718 if ( mapLayerPtr )
4719 {
4720 ogcContext.layer = mapLayerPtr.data();
4721 ogcContext.transformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
4722 }
4723 }
4724 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4725 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4726 return result;
4727}
4728
4729static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4730{
4731 FEAT_FROM_CONTEXT( context, f )
4733 QgsDistanceArea *calc = parent->geomCalculator();
4734 if ( calc )
4735 {
4736 try
4737 {
4738 double area = calc->measureArea( f.geometry() );
4739 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4740 return QVariant( area );
4741 }
4742 catch ( QgsCsException & )
4743 {
4744 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4745 return QVariant();
4746 }
4747 }
4748 else
4749 {
4750 return QVariant( f.geometry().area() );
4751 }
4752}
4753
4754static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4755{
4756 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4757
4758 if ( geom.type() != Qgis::GeometryType::Polygon )
4759 return QVariant();
4760
4761 return QVariant( geom.area() );
4762}
4763
4764static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4765{
4766 FEAT_FROM_CONTEXT( context, f )
4768 QgsDistanceArea *calc = parent->geomCalculator();
4769 if ( calc )
4770 {
4771 try
4772 {
4773 double len = calc->measureLength( f.geometry() );
4774 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4775 return QVariant( len );
4776 }
4777 catch ( QgsCsException & )
4778 {
4779 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4780 return QVariant();
4781 }
4782 }
4783 else
4784 {
4785 return QVariant( f.geometry().length() );
4786 }
4787}
4788
4789static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4790{
4791 FEAT_FROM_CONTEXT( context, f )
4793 QgsDistanceArea *calc = parent->geomCalculator();
4794 if ( calc )
4795 {
4796 try
4797 {
4798 double len = calc->measurePerimeter( f.geometry() );
4799 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4800 return QVariant( len );
4801 }
4802 catch ( QgsCsException & )
4803 {
4804 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4805 return QVariant();
4806 }
4807 }
4808 else
4809 {
4810 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4811 }
4812}
4813
4814static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4815{
4816 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4817
4818 if ( geom.type() != Qgis::GeometryType::Polygon )
4819 return QVariant();
4820
4821 //length for polygons = perimeter
4822 return QVariant( geom.length() );
4823}
4824
4825static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4826{
4827 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4828 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4829}
4830
4831static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4832{
4833 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4834 if ( geom.isNull() )
4835 return QVariant();
4836
4837 return QVariant( geom.constGet()->partCount() );
4838}
4839
4840static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4841{
4842 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4843 if ( geom.isNull() )
4844 return QVariant();
4845
4846 return QVariant( geom.isMultipart() );
4847}
4848
4849static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4850{
4851 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4852
4853 if ( geom.isNull() )
4854 return QVariant();
4855
4857 if ( curvePolygon )
4858 return QVariant( curvePolygon->numInteriorRings() );
4859
4861 if ( collection )
4862 {
4863 //find first CurvePolygon in collection
4864 for ( int i = 0; i < collection->numGeometries(); ++i )
4865 {
4866 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4867 if ( !curvePolygon )
4868 continue;
4869
4870 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4871 }
4872 }
4873
4874 return QVariant();
4875}
4876
4877static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4878{
4879 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4880
4881 if ( geom.isNull() )
4882 return QVariant();
4883
4885 if ( curvePolygon )
4886 return QVariant( curvePolygon->ringCount() );
4887
4888 bool foundPoly = false;
4889 int ringCount = 0;
4891 if ( collection )
4892 {
4893 //find CurvePolygons in collection
4894 for ( int i = 0; i < collection->numGeometries(); ++i )
4895 {
4896 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4897 if ( !curvePolygon )
4898 continue;
4899
4900 foundPoly = true;
4901 ringCount += curvePolygon->ringCount();
4902 }
4903 }
4904
4905 if ( !foundPoly )
4906 return QVariant();
4907
4908 return QVariant( ringCount );
4909}
4910
4911static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4912{
4913 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4914 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4915 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4916 return result;
4917}
4918
4919static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4920{
4921 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4922 return QVariant::fromValue( geom.boundingBox().width() );
4923}
4924
4925static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4926{
4927 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4928 return QVariant::fromValue( geom.boundingBox().height() );
4929}
4930
4931static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4932{
4933 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4934 if ( geom.isNull() )
4935 return QVariant();
4936
4938}
4939
4940static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4941{
4942 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4943 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4944}
4945
4946static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4947{
4948 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4949 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4950}
4951
4952static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4953{
4954 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4955 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4956}
4957
4958static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4959{
4960 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4961 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4962}
4963
4964static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4965{
4966 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4967
4968 if ( geom.isNull() || geom.isEmpty( ) )
4969 return QVariant();
4970
4971 if ( !geom.constGet()->is3D() )
4972 return QVariant();
4973
4974 double max = std::numeric_limits< double >::lowest();
4975
4976 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4977 {
4978 double z = ( *it ).z();
4979
4980 if ( max < z )
4981 max = z;
4982 }
4983
4984 if ( max == std::numeric_limits< double >::lowest() )
4985 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4986
4987 return QVariant( max );
4988}
4989
4990static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4991{
4992 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4993
4994 if ( geom.isNull() || geom.isEmpty() )
4995 return QVariant();
4996
4997 if ( !geom.constGet()->is3D() )
4998 return QVariant();
4999
5000 double min = std::numeric_limits< double >::max();
5001
5002 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5003 {
5004 double z = ( *it ).z();
5005
5006 if ( z < min )
5007 min = z;
5008 }
5009
5010 if ( min == std::numeric_limits< double >::max() )
5011 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5012
5013 return QVariant( min );
5014}
5015
5016static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5017{
5018 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5019
5020 if ( geom.isNull() || geom.isEmpty() )
5021 return QVariant();
5022
5023 if ( !geom.constGet()->isMeasure() )
5024 return QVariant();
5025
5026 double min = std::numeric_limits< double >::max();
5027
5028 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5029 {
5030 double m = ( *it ).m();
5031
5032 if ( m < min )
5033 min = m;
5034 }
5035
5036 if ( min == std::numeric_limits< double >::max() )
5037 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5038
5039 return QVariant( min );
5040}
5041
5042static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5043{
5044 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5045
5046 if ( geom.isNull() || geom.isEmpty() )
5047 return QVariant();
5048
5049 if ( !geom.constGet()->isMeasure() )
5050 return QVariant();
5051
5052 double max = std::numeric_limits< double >::lowest();
5053
5054 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5055 {
5056 double m = ( *it ).m();
5057
5058 if ( max < m )
5059 max = m;
5060 }
5061
5062 if ( max == std::numeric_limits< double >::lowest() )
5063 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5064
5065 return QVariant( max );
5066}
5067
5068static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5069{
5070 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5072 if ( !curve )
5073 {
5074 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
5075 return QVariant();
5076 }
5077
5078 return QVariant( curve->sinuosity() );
5079}
5080
5081static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5082{
5083 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5084 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
5085 if ( !curve )
5086 {
5087 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
5088 return QVariant();
5089 }
5090
5091 return QVariant( curve->straightDistance2d() );
5092}
5093
5094static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5095{
5096 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5098
5099 if ( !poly )
5100 {
5101 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
5102 return QVariant();
5103 }
5104
5105 return QVariant( poly->roundness() );
5106}
5107
5108
5109
5110static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5111{
5112 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5113 if ( geom.isNull() )
5114 return QVariant();
5115
5116 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
5117 flipped->swapXy();
5118 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
5119}
5120
5121static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5122{
5123 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5124 if ( fGeom.isNull() )
5125 return QVariant();
5126
5127 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
5128 if ( !curve && fGeom.isMultipart() )
5129 {
5131 {
5132 if ( collection->numGeometries() == 1 )
5133 {
5134 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5135 }
5136 }
5137 }
5138
5139 if ( !curve )
5140 return QVariant();
5141
5142 return QVariant::fromValue( curve->isClosed() );
5143}
5144
5145static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5146{
5147 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5148
5149 if ( geom.isNull() )
5150 return QVariant();
5151
5152 QVariant result;
5153 if ( !geom.isMultipart() )
5154 {
5156
5157 if ( !line )
5158 return QVariant();
5159
5160 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5161 closedLine->close();
5162
5163 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
5164 }
5165 else
5166 {
5168 if ( !collection )
5169 return QVariant();
5170
5171 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
5172
5173 for ( int i = 0; i < collection->numGeometries(); ++i )
5174 {
5175 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
5176 {
5177 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5178 closedLine->close();
5179
5180 closed->addGeometry( closedLine.release() );
5181 }
5182 }
5183 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
5184 }
5185
5186 return result;
5187}
5188
5189static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5190{
5191 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5192 if ( fGeom.isNull() )
5193 return QVariant();
5194
5195 return QVariant::fromValue( fGeom.isEmpty() );
5196}
5197
5198static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5199{
5200 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5201 return QVariant::fromValue( true );
5202
5203 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5204 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
5205}
5206
5207static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5208{
5209 if ( values.length() < 2 || values.length() > 3 )
5210 return QVariant();
5211
5212 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5213 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5214
5215 if ( fGeom.isNull() || sGeom.isNull() )
5216 return QVariant();
5217
5218 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
5219
5220 if ( values.length() == 2 )
5221 {
5222 //two geometry arguments, return relation
5223 QString result = engine->relate( sGeom.constGet() );
5224 return QVariant::fromValue( result );
5225 }
5226 else
5227 {
5228 //three arguments, test pattern
5229 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5230 bool result = engine->relatePattern( sGeom.constGet(), pattern );
5231 return QVariant::fromValue( result );
5232 }
5233}
5234
5235static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5236{
5237 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5238 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5239 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
5240}
5241static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5242{
5243 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5244 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5245 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
5246}
5247static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5248{
5249 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5250 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5251 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
5252}
5253static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5254{
5255 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5256 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5257 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
5258}
5259static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5260{
5261 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5262 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5263 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
5264}
5265static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5266{
5267 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5268 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5269 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
5270}
5271static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5272{
5273 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5274 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5275 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
5276}
5277static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5278{
5279 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5280 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5281 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
5282}
5283
5284static QVariant fcnEquals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5285{
5286 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5287 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5288 return fGeom.equals( sGeom ) ? TVL_True : TVL_False;
5289}
5290
5291static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5292{
5293 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5294 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5295 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5296 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
5297 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
5298 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5299
5301 if ( endCapString.compare( "flat"_L1, Qt::CaseInsensitive ) == 0 )
5302 capStyle = Qgis::EndCapStyle::Flat;
5303 else if ( endCapString.compare( "square"_L1, Qt::CaseInsensitive ) == 0 )
5304 capStyle = Qgis::EndCapStyle::Square;
5305
5307 if ( joinString.compare( "miter"_L1, Qt::CaseInsensitive ) == 0 )
5308 joinStyle = Qgis::JoinStyle::Miter;
5309 else if ( joinString.compare( "bevel"_L1, Qt::CaseInsensitive ) == 0 )
5310 joinStyle = Qgis::JoinStyle::Bevel;
5311
5312 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
5313 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5314 return result;
5315}
5316
5317static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5318{
5319 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5320 const QgsGeometry reoriented = fGeom.forceRHR();
5321 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5322}
5323
5324static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5325{
5326 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5327 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
5328 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5329}
5330
5331static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5332{
5333 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5334 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
5335 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5336}
5337
5338static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5339{
5340 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5342 if ( !pt && fGeom.isMultipart() )
5343 {
5345 {
5346 if ( collection->numGeometries() == 1 )
5347 {
5348 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5349 }
5350 }
5351 }
5352
5353 if ( !pt )
5354 {
5355 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
5356 return QVariant();
5357 }
5358
5359 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5360 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5361 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5362 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5363
5364 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
5365 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5366 return result;
5367}
5368
5369static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5370{
5371 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5372 if ( fGeom.type() != Qgis::GeometryType::Line )
5373 {
5374 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
5375 return QVariant();
5376 }
5377
5378 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5379 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5380 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
5381
5382 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
5383 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5384 return result;
5385}
5386
5387static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5388{
5389 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5390 if ( fGeom.type() != Qgis::GeometryType::Line )
5391 {
5392 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
5393 return QVariant();
5394 }
5395
5396 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
5397
5398 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
5399 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5400 return result;
5401}
5402
5403static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5404{
5405 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5406 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5407 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5408 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5409 if ( joinInt < 1 || joinInt > 3 )
5410 return QVariant();
5411 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5412
5413 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5414
5415 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
5416 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5417 return result;
5418}
5419
5420static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5421{
5422 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5423 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5424 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5425
5426 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5427 if ( joinInt < 1 || joinInt > 3 )
5428 return QVariant();
5429 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5430
5431 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5432
5433 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5434 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5435 return result;
5436}
5437
5438static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5439{
5440 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5441 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5442 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5443
5444 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
5445 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5446 return result;
5447}
5448
5449static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5450{
5451 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5452 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5453 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5454 fGeom.translate( dx, dy );
5455 return QVariant::fromValue( fGeom );
5456}
5457
5458static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5459{
5460 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5461 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5462 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
5463 : QgsGeometry();
5464 const bool perPart = values.value( 3 ).toBool();
5465
5466 if ( center.isNull() && perPart && fGeom.isMultipart() )
5467 {
5468 // no explicit center, rotating per part
5469 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5470 // the result is equivalent to setting perPart as false anyway)
5471 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5472 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5473 {
5474 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5475 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5476 t.rotate( -rotation );
5477 t.translate( -partCenter.x(), -partCenter.y() );
5478 ( *it )->transform( t );
5479 }
5480 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5481 }
5482 else
5483 {
5484 QgsPointXY pt;
5485 if ( center.isEmpty() )
5486 {
5487 // if center wasn't specified, use bounding box centroid
5488 pt = fGeom.boundingBox().center();
5489 }
5491 {
5492 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5493 return QVariant();
5494 }
5495 else
5496 {
5498 }
5499
5500 fGeom.rotate( rotation, pt );
5501 return QVariant::fromValue( fGeom );
5502 }
5503}
5504
5505static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5506{
5507 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5508 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5509 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5510 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
5511 : QgsGeometry();
5512
5513 QgsPointXY pt;
5514 if ( center.isNull() )
5515 {
5516 // if center wasn't specified, use bounding box centroid
5517 pt = fGeom.boundingBox().center();
5518 }
5520 {
5521 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5522 return QVariant();
5523 }
5524 else
5525 {
5526 pt = center.asPoint();
5527 }
5528
5529 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5530 t.scale( xScale, yScale );
5531 t.translate( -pt.x(), -pt.y() );
5532 fGeom.transform( t );
5533 return QVariant::fromValue( fGeom );
5534}
5535
5536static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5537{
5538 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5539 if ( fGeom.isNull() )
5540 {
5541 return QVariant();
5542 }
5543
5544 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5545 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5546
5547 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5548
5549 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5550 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5551
5552 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5553 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5554 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5555 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5556
5557 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5558 {
5559 fGeom.get()->addZValue( 0 );
5560 }
5561 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5562 {
5563 fGeom.get()->addMValue( 0 );
5564 }
5565
5566 QTransform transform;
5567 transform.translate( deltaX, deltaY );
5568 transform.rotate( rotationZ );
5569 transform.scale( scaleX, scaleY );
5570 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5571
5572 return QVariant::fromValue( fGeom );
5573}
5574
5575
5576static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5577{
5578 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5579 QgsGeometry geom = fGeom.centroid();
5580 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5581 return result;
5582}
5583static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5584{
5585 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5586 QgsGeometry geom = fGeom.pointOnSurface();
5587 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5588 return result;
5589}
5590
5591static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5592{
5593 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5594 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5595 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5596 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5597 return result;
5598}
5599
5600static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5601{
5602 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5603 QgsGeometry geom = fGeom.convexHull();
5604 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5605 return result;
5606}
5607
5608#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
5609static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5610{
5611 try
5612 {
5613 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5614 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5615 const bool allowHoles = values.value( 2 ).toBool();
5616 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5617 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5618 return result;
5619 }
5620 catch ( QgsCsException &cse )
5621 {
5622 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5623 return QVariant();
5624 }
5625}
5626#endif
5627
5628static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5629{
5630 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5631 int segments = 36;
5632 if ( values.length() == 2 )
5633 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5634 if ( segments < 0 )
5635 {
5636 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5637 return QVariant();
5638 }
5639
5640 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5641 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5642 return result;
5643}
5644
5645static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5646{
5647 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5649 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5650 return result;
5651}
5652
5653static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5654{
5655 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5656
5657 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5658 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5659 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5660
5661 double area, angle, width, height;
5662 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5663
5664 if ( geom.isNull() )
5665 {
5666 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5667 return QVariant();
5668 }
5669 return angle;
5670}
5671
5672static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5673{
5674 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5675 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5676 QgsGeometry geom = fGeom.difference( sGeom );
5677 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5678 return result;
5679}
5680
5681static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5682{
5683 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5684 return QVariant();
5685
5686 // two variants, one for geometry, one for string
5687
5688 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5689 if ( !fGeom.isNull() )
5690 {
5691 QVariant result;
5692 if ( !fGeom.isMultipart() )
5693 {
5694 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5695 if ( !curve )
5696 return QVariant();
5697
5698 QgsCurve *reversed = curve->reversed();
5699 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5700 }
5701 else
5702 {
5704 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5705 for ( int i = 0; i < collection->numGeometries(); ++i )
5706 {
5707 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5708 {
5709 reversed->addGeometry( curve->reversed() );
5710 }
5711 else
5712 {
5713 reversed->addGeometry( collection->geometryN( i )->clone() );
5714 }
5715 }
5716 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5717 }
5718 return result;
5719 }
5720
5721 //fall back to string variant
5722 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5723 std::reverse( string.begin(), string.end() );
5724 return string;
5725}
5726
5727static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5728{
5729 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5730 if ( fGeom.isNull() )
5731 return QVariant();
5732
5734 if ( !curvePolygon && fGeom.isMultipart() )
5735 {
5737 {
5738 if ( collection->numGeometries() == 1 )
5739 {
5740 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5741 }
5742 }
5743 }
5744
5745 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5746 return QVariant();
5747
5748 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5749 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5750 return result;
5751}
5752
5753static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5754{
5755 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5756 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5757 return QVariant( fGeom.distance( sGeom ) );
5758}
5759
5760static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5761{
5762 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5763 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5764
5765 double res = -1;
5766 if ( values.length() == 3 && values.at( 2 ).isValid() )
5767 {
5768 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5769 densify = std::clamp( densify, 0.0, 1.0 );
5770 res = g1.hausdorffDistanceDensify( g2, densify );
5771 }
5772 else
5773 {
5774 res = g1.hausdorffDistance( g2 );
5775 }
5776
5777 return res > -1 ? QVariant( res ) : QVariant();
5778}
5779
5780static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5781{
5782 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5783 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5784 QgsGeometry geom = fGeom.intersection( sGeom );
5785 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5786 return result;
5787}
5788static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5789{
5790 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5791 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5792 QgsGeometry geom = fGeom.symDifference( sGeom );
5793 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5794 return result;
5795}
5796static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5797{
5798 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5799 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5800 QgsGeometry geom = fGeom.combine( sGeom );
5801 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5802 return result;
5803}
5804
5805static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5806{
5807 if ( values.length() < 1 || values.length() > 2 )
5808 return QVariant();
5809
5810 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5811 int prec = 8;
5812 if ( values.length() == 2 )
5813 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5814 QString wkt = fGeom.asWkt( prec );
5815 return QVariant( wkt );
5816}
5817
5818static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5819{
5820 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5821 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5822}
5823
5824static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5825{
5826 if ( values.length() != 2 )
5827 {
5828 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5829 return QVariant();
5830 }
5831
5832 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5833 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5834
5835 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5836 if ( !pt1 && fGeom1.isMultipart() )
5837 {
5839 {
5840 if ( collection->numGeometries() == 1 )
5841 {
5842 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5843 }
5844 }
5845 }
5846
5847 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5848 if ( !pt2 && fGeom2.isMultipart() )
5849 {
5851 {
5852 if ( collection->numGeometries() == 1 )
5853 {
5854 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5855 }
5856 }
5857 }
5858
5859 if ( !pt1 || !pt2 )
5860 {
5861 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5862 return QVariant();
5863 }
5864
5865 // Code from PostGIS
5866 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5867 {
5868 if ( pt1->y() < pt2->y() )
5869 return 0.0;
5870 else if ( pt1->y() > pt2->y() )
5871 return M_PI;
5872 else
5873 return 0;
5874 }
5875
5876 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5877 {
5878 if ( pt1->x() < pt2->x() )
5879 return M_PI_2;
5880 else if ( pt1->x() > pt2->x() )
5881 return M_PI + ( M_PI_2 );
5882 else
5883 return 0;
5884 }
5885
5886 if ( pt1->x() < pt2->x() )
5887 {
5888 if ( pt1->y() < pt2->y() )
5889 {
5890 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5891 }
5892 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5893 {
5894 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5895 + ( M_PI_2 );
5896 }
5897 }
5898
5899 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5900 {
5901 if ( pt1->y() > pt2->y() )
5902 {
5903 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
5904 + M_PI;
5905 }
5906 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5907 {
5908 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5909 + ( M_PI + ( M_PI_2 ) );
5910 }
5911 }
5912}
5913
5914static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5915{
5916 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5917 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5918 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
5919 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
5920
5921 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
5922 {
5923 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
5924 return QVariant();
5925 }
5926
5927 const QgsPointXY point1 = geom1.asPoint();
5928 const QgsPointXY point2 = geom2.asPoint();
5929 if ( point1.isEmpty() || point2.isEmpty() )
5930 {
5931 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
5932 return QVariant();
5933 }
5934
5936 if ( context )
5937 {
5938 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
5939
5940 if ( !sourceCrs.isValid() )
5941 {
5942 sourceCrs = context->variable( u"_layer_crs"_s ).value<QgsCoordinateReferenceSystem>();
5943 }
5944
5945 if ( ellipsoid.isEmpty() )
5946 {
5947 ellipsoid = context->variable( u"project_ellipsoid"_s ).toString();
5948 }
5949 }
5950
5951 if ( !sourceCrs.isValid() )
5952 {
5953 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
5954 return QVariant();
5955 }
5956
5957 QgsDistanceArea da;
5958 da.setSourceCrs( sourceCrs, tContext );
5959 if ( !da.setEllipsoid( ellipsoid ) )
5960 {
5961 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
5962 return QVariant();
5963 }
5964
5965 try
5966 {
5967 const double bearing = da.bearing( point1, point2 );
5968 if ( std::isfinite( bearing ) )
5969 {
5970 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
5971 }
5972 }
5973 catch ( QgsCsException &cse )
5974 {
5975 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
5976 return QVariant();
5977 }
5978 return QVariant();
5979}
5980
5981static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5982{
5983 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5984
5986 {
5987 parent->setEvalErrorString( u"'project' requires a point geometry"_s );
5988 return QVariant();
5989 }
5990
5991 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5992 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5993 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5994
5995 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
5996 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5997
5998 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5999}
6000
6001static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6002{
6003 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6004 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6005
6006 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6007 if ( !pt1 && fGeom1.isMultipart() )
6008 {
6010 {
6011 if ( collection->numGeometries() == 1 )
6012 {
6013 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6014 }
6015 }
6016 }
6017 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6018 if ( !pt2 && fGeom2.isMultipart() )
6019 {
6021 {
6022 if ( collection->numGeometries() == 1 )
6023 {
6024 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6025 }
6026 }
6027 }
6028
6029 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) ||
6030 !pt1 || !pt2 )
6031 {
6032 parent->setEvalErrorString( u"Function 'inclination' requires two points as arguments."_s );
6033 return QVariant();
6034 }
6035
6036 return pt1->inclination( *pt2 );
6037
6038}
6039
6040static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6041{
6042 if ( values.length() != 3 )
6043 return QVariant();
6044
6045 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6046 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6047 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6048
6049 QgsGeometry geom = fGeom.extrude( x, y );
6050
6051 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
6052 return result;
6053}
6054
6055static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
6056{
6057 if ( values.length() < 2 )
6058 return QVariant();
6059
6060 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6061
6062 if ( !fGeom.isMultipart() )
6063 return values.at( 0 );
6064
6065 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6066 QVariant cachedExpression;
6067 if ( ctx )
6068 cachedExpression = ctx->cachedValue( expString );
6069 QgsExpression expression;
6070
6071 if ( cachedExpression.isValid() )
6072 {
6073 expression = cachedExpression.value<QgsExpression>();
6074 }
6075 else
6076 expression = QgsExpression( expString );
6077
6078 bool asc = values.value( 2 ).toBool();
6079
6080 QgsExpressionContext *unconstedContext = nullptr;
6081 QgsFeature f;
6082 if ( ctx )
6083 {
6084 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
6085 // so no reason to worry
6086 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
6087 f = ctx->feature();
6088 }
6089 else
6090 {
6091 // If there's no context provided, create a fake one
6092 unconstedContext = new QgsExpressionContext();
6093 }
6094
6096 Q_ASSERT( collection ); // Should have failed the multipart check above
6097
6099 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
6100 QgsExpressionSorter sorter( orderBy );
6101
6102 QList<QgsFeature> partFeatures;
6103 partFeatures.reserve( collection->partCount() );
6104 for ( int i = 0; i < collection->partCount(); ++i )
6105 {
6106 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
6107 partFeatures << f;
6108 }
6109
6110 sorter.sortFeatures( partFeatures, unconstedContext );
6111
6113
6114 Q_ASSERT( orderedGeom );
6115
6116 while ( orderedGeom->partCount() )
6117 orderedGeom->removeGeometry( 0 );
6118
6119 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
6120 {
6121 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
6122 }
6123
6124 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
6125
6126 if ( !ctx )
6127 delete unconstedContext;
6128
6129 return result;
6130}
6131
6132static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6133{
6134 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6135 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6136
6137 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
6138
6139 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6140 return result;
6141}
6142
6143static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6144{
6145 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6146 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6147
6148 QgsGeometry geom = fromGeom.shortestLine( toGeom );
6149
6150 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6151 return result;
6152}
6153
6154static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6155{
6156 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6157 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6158
6159 QgsGeometry geom = lineGeom.interpolate( distance );
6160
6161 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6162 return result;
6163}
6164
6165static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6166{
6167 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6168 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6169 const bool use3DDistance = values.at( 2 ).toBool();
6170
6171 double x, y, z, distance;
6172
6174 if ( !line )
6175 {
6176 return QVariant();
6177 }
6178
6179 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
6180 {
6181 QgsPoint point( x, y );
6182 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
6183 {
6184 point.addZValue( z );
6185 }
6186 return QVariant::fromValue( QgsGeometry( point.clone() ) );
6187 }
6188
6189 return QVariant();
6190}
6191
6192static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6193{
6194 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6195 if ( lineGeom.type() != Qgis::GeometryType::Line )
6196 {
6197 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
6198 return QVariant();
6199 }
6200
6201 const QgsCurve *curve = nullptr;
6202 if ( !lineGeom.isMultipart() )
6203 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
6204 else
6205 {
6207 {
6208 if ( collection->numGeometries() > 0 )
6209 {
6210 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
6211 }
6212 }
6213 }
6214 if ( !curve )
6215 return QVariant();
6216
6217 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6218 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6219
6220 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
6221 QgsGeometry result( std::move( substring ) );
6222 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
6223}
6224
6225static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6226{
6227 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6228 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6229
6230 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
6231}
6232
6233static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6234{
6235 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6236 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6237 if ( vertex < 0 )
6238 {
6239 //negative idx
6240 int count = geom.constGet()->nCoordinates();
6241 vertex = count + vertex;
6242 }
6243
6244 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
6245}
6246
6247static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6248{
6249 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6250 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6251 if ( vertex < 0 )
6252 {
6253 //negative idx
6254 int count = geom.constGet()->nCoordinates();
6255 vertex = count + vertex;
6256 }
6257
6258 return geom.distanceToVertex( vertex );
6259}
6260
6261static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6262{
6263 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6264 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6265
6266 double distance = lineGeom.lineLocatePoint( pointGeom );
6267
6268 return distance >= 0 ? distance : QVariant();
6269}
6270
6271static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6272{
6273 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6274 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6275 const bool use3DDistance = values.at( 2 ).toBool();
6276
6277 double x, y, z, distance;
6278
6280 if ( !line )
6281 {
6282 return QVariant();
6283 }
6284
6285 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
6286 return found ? distance : QVariant();
6287}
6288
6289static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6290{
6291 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
6292 {
6293 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6294 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6295 }
6296
6297 if ( values.length() >= 1 )
6298 {
6299 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6300 return QVariant( qlonglong( std::round( number ) ) );
6301 }
6302
6303 return QVariant();
6304}
6305
6306static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6307{
6308 Q_UNUSED( values )
6309 Q_UNUSED( parent )
6310 return M_PI;
6311}
6312
6313static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6314{
6315 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6316 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6317 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6318 if ( places < 0 )
6319 {
6320 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
6321 return QVariant();
6322 }
6323
6324 const bool omitGroupSeparator = values.value( 3 ).toBool();
6325 const bool trimTrailingZeros = values.value( 4 ).toBool();
6326
6327 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6328 if ( !omitGroupSeparator )
6329 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
6330 else
6331 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
6332
6333 QString res = locale.toString( value, 'f', places );
6334
6335 if ( trimTrailingZeros )
6336 {
6337 const QChar decimal = locale.decimalPoint().at( 0 );
6338 const QChar zeroDigit = locale.zeroDigit().at( 0 );
6339
6340 if ( res.contains( decimal ) )
6341 {
6342 int trimPoint = res.length() - 1;
6343
6344 while ( res.at( trimPoint ) == zeroDigit )
6345 trimPoint--;
6346
6347 if ( res.at( trimPoint ) == decimal )
6348 trimPoint--;
6349
6350 res.truncate( trimPoint + 1 );
6351 }
6352 }
6353
6354 return res;
6355}
6356
6357static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6358{
6359 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
6360 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6361 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6362
6363 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
6364 if ( format.indexOf( "Z" ) > 0 )
6365 datetime = datetime.toUTC();
6366
6367 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6368 return locale.toString( datetime, format );
6369}
6370
6371static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6372{
6373 const QVariant variant = values.at( 0 );
6374 bool isQColor;
6375 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6376 if ( !color.isValid() )
6377 return QVariant();
6378
6379 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6380 if ( color.spec() == QColor::Spec::Cmyk )
6381 {
6382 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6383 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
6384 }
6385 else
6386 {
6387 const float avg = ( color.redF() + color.greenF() + color.blueF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6388 color.setRgbF( avg, avg, avg, alpha );
6389 }
6390
6391 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6392}
6393
6394static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6395{
6396 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6397 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
6398 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6399 if ( ratio > 1 )
6400 {
6401 ratio = 1;
6402 }
6403 else if ( ratio < 0 )
6404 {
6405 ratio = 0;
6406 }
6407
6408 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
6409 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
6410 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
6411 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
6412
6413 QColor newColor( red, green, blue, alpha );
6414
6415 return QgsSymbolLayerUtils::encodeColor( newColor );
6416}
6417
6418static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6419{
6420 const QVariant variant1 = values.at( 0 );
6421 const QVariant variant2 = values.at( 1 );
6422
6423 if ( variant1.userType() != variant2.userType() )
6424 {
6425 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6426 return QVariant();
6427 }
6428
6429 bool isQColor;
6430 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6431 if ( !color1.isValid() )
6432 return QVariant();
6433
6434 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6435 if ( !color2.isValid() )
6436 return QVariant();
6437
6438 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6439 {
6440 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6441 return QVariant();
6442 }
6443
6444 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6445
6446 // TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6447 // NOLINTBEGIN(bugprone-narrowing-conversions)
6448
6449 QColor newColor;
6450 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6451 if ( color1.spec() == QColor::Spec::Cmyk )
6452 {
6453 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6454 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6455 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6456 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6457 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6458 }
6459 else
6460 {
6461 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6462 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6463 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6464 newColor = QColor::fromRgbF( red, green, blue, alpha );
6465 }
6466
6467 // NOLINTEND(bugprone-narrowing-conversions)
6468
6469 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6470}
6471
6472static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6473{
6474 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6475 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6476 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6477 QColor color = QColor( red, green, blue );
6478 if ( ! color.isValid() )
6479 {
6480 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6481 color = QColor( 0, 0, 0 );
6482 }
6483
6484 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6485}
6486
6487static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6488{
6489 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6490 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6491 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6492 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6493 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6494 if ( ! color.isValid() )
6495 {
6496 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6497 return QVariant();
6498 }
6499
6500 return color;
6501}
6502
6503static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6504{
6505 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6506 QVariant value = node->eval( parent, context );
6507 if ( parent->hasEvalError() )
6508 {
6509 parent->setEvalErrorString( QString() );
6510 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6512 value = node->eval( parent, context );
6514 }
6515 return value;
6516}
6517
6518static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6519{
6520 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6522 QVariant value = node->eval( parent, context );
6524 if ( value.toBool() )
6525 {
6526 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6528 value = node->eval( parent, context );
6530 }
6531 else
6532 {
6533 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6535 value = node->eval( parent, context );
6537 }
6538 return value;
6539}
6540
6541static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6542{
6543 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6544 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6545 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6546 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6547 QColor color = QColor( red, green, blue, alpha );
6548 if ( ! color.isValid() )
6549 {
6550 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6551 color = QColor( 0, 0, 0 );
6552 }
6553 return QgsSymbolLayerUtils::encodeColor( color );
6554}
6555
6556QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6557{
6558 QgsGradientColorRamp expRamp;
6559 const QgsColorRamp *ramp = nullptr;
6560 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6561 {
6562 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6563 ramp = &expRamp;
6564 }
6565 else
6566 {
6567 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6568 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6569 if ( ! ramp )
6571 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6572 return QVariant();
6573 }
6574 }
6575
6576 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6577 QColor color = ramp->color( value );
6578 return color;
6579}
6580
6581QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6582{
6583 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6584 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6585}
6586
6587static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6588{
6589 // Hue ranges from 0 - 360
6590 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6591 // Saturation ranges from 0 - 100
6592 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6593 // Lightness ranges from 0 - 100
6594 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6595
6596 QColor color = QColor::fromHslF( hue, saturation, lightness );
6597
6598 if ( ! color.isValid() )
6599 {
6600 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6601 color = QColor( 0, 0, 0 );
6602 }
6603
6604 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6605}
6606
6607static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6608{
6609 // Hue ranges from 0 - 360
6610 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6611 // Saturation ranges from 0 - 100
6612 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6613 // Lightness ranges from 0 - 100
6614 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6615 // Alpha ranges from 0 - 255
6616 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6617
6618 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6619 if ( ! color.isValid() )
6620 {
6621 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6622 color = QColor( 0, 0, 0 );
6623 }
6624 return QgsSymbolLayerUtils::encodeColor( color );
6625}
6626
6627static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6628{
6629 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6630 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6631 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6632 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6633
6634 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6635 if ( ! color.isValid() )
6636 {
6637 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6638 return QVariant();
6639 }
6640
6641 return color;
6642}
6643
6644static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6645{
6646 // Hue ranges from 0 - 360
6647 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6648 // Saturation ranges from 0 - 100
6649 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6650 // Value ranges from 0 - 100
6651 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6652
6653 QColor color = QColor::fromHsvF( hue, saturation, value );
6654
6655 if ( ! color.isValid() )
6656 {
6657 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6658 color = QColor( 0, 0, 0 );
6659 }
6660
6661 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6662}
6663
6664static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6665{
6666 // Hue ranges from 0 - 360
6667 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6668 // Saturation ranges from 0 - 100
6669 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6670 // Value ranges from 0 - 100
6671 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6672 // Alpha ranges from 0 - 255
6673 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6674
6675 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6676 if ( ! color.isValid() )
6677 {
6678 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6679 color = QColor( 0, 0, 0 );
6680 }
6681 return QgsSymbolLayerUtils::encodeColor( color );
6682}
6683
6684static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6685{
6686 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6687 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6688 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6689 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6690 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6691
6692 if ( ! color.isValid() )
6693 {
6694 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6695 return QVariant();
6696 }
6697
6698 return color;
6699}
6700
6701static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6702{
6703 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6704 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6705 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6706 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6707 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6708
6709 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6710 if ( ! color.isValid() )
6711 {
6712 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6713 return QVariant();
6714 }
6715
6716 return color;
6717}
6718
6719static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6720{
6721 // Cyan ranges from 0 - 100
6722 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6723 // Magenta ranges from 0 - 100
6724 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6725 // Yellow ranges from 0 - 100
6726 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6727 // Black ranges from 0 - 100
6728 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6729
6730 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6731
6732 if ( ! color.isValid() )
6733 {
6734 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6735 color = QColor( 0, 0, 0 );
6736 }
6737
6738 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6739}
6740
6741static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6742{
6743 // Cyan ranges from 0 - 100
6744 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6745 // Magenta ranges from 0 - 100
6746 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6747 // Yellow ranges from 0 - 100
6748 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6749 // Black ranges from 0 - 100
6750 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6751 // Alpha ranges from 0 - 255
6752 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6753
6754 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6755 if ( ! color.isValid() )
6756 {
6757 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6758 color = QColor( 0, 0, 0 );
6759 }
6760 return QgsSymbolLayerUtils::encodeColor( color );
6761}
6762
6763static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6764{
6765 const QVariant variant = values.at( 0 );
6766 bool isQColor;
6767 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6768 if ( !color.isValid() )
6769 return QVariant();
6770
6771 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6772 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6773 return color.red();
6774 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
6775 return color.green();
6776 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
6777 return color.blue();
6778 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
6779 return color.alpha();
6780 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
6781 return static_cast< double >( color.hsvHueF() * 360 );
6782 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
6783 return static_cast< double >( color.hsvSaturationF() * 100 );
6784 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
6785 return static_cast< double >( color.valueF() * 100 );
6786 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
6787 return static_cast< double >( color.hslHueF() * 360 );
6788 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
6789 return static_cast< double >( color.hslSaturationF() * 100 );
6790 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
6791 return static_cast< double >( color.lightnessF() * 100 );
6792 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
6793 return static_cast< double >( color.cyanF() * 100 );
6794 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
6795 return static_cast< double >( color.magentaF() * 100 );
6796 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
6797 return static_cast< double >( color.yellowF() * 100 );
6798 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
6799 return static_cast< double >( color.blackF() * 100 );
6800
6801 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6802 return QVariant();
6803}
6804
6805static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6806{
6807 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6808 if ( map.empty() )
6809 {
6810 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6811 return QVariant();
6812 }
6813
6814 QList< QColor > colors;
6816 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6817 {
6818 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
6819 if ( !colors.last().isValid() )
6820 {
6821 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
6822 return QVariant();
6823 }
6824
6825 double step = it.key().toDouble();
6826 if ( it == map.constBegin() )
6827 {
6828 if ( step != 0.0 )
6829 stops << QgsGradientStop( step, colors.last() );
6830 }
6831 else if ( it == map.constEnd() )
6832 {
6833 if ( step != 1.0 )
6834 stops << QgsGradientStop( step, colors.last() );
6835 }
6836 else
6837 {
6838 stops << QgsGradientStop( step, colors.last() );
6839 }
6840 }
6841 bool discrete = values.at( 1 ).toBool();
6842
6843 if ( colors.empty() )
6844 return QVariant();
6845
6846 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
6847}
6848
6849static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6850{
6851 const QVariant variant = values.at( 0 );
6852 bool isQColor;
6853 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6854 if ( !color.isValid() )
6855 return QVariant();
6856
6857 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6858 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6859 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6860 color.setRed( std::clamp( value, 0, 255 ) );
6861 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
6862 color.setGreen( std::clamp( value, 0, 255 ) );
6863 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
6864 color.setBlue( std::clamp( value, 0, 255 ) );
6865 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
6866 color.setAlpha( std::clamp( value, 0, 255 ) );
6867 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
6868 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
6869 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
6870 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
6871 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
6872 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6873 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
6874 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
6875 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
6876 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
6877 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
6878 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6879 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
6880 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
6881 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
6882 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
6883 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
6884 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
6885 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
6886 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6887 else
6888 {
6889 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6890 return QVariant();
6891 }
6892 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6893}
6894
6895static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6896{
6897 const QVariant variant = values.at( 0 );
6898 bool isQColor;
6899 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6900 if ( !color.isValid() )
6901 return QVariant();
6902
6903 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6904
6905 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6906}
6907
6908static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6909{
6910 const QVariant variant = values.at( 0 );
6911 bool isQColor;
6912 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6913 if ( !color.isValid() )
6914 return QVariant();
6915
6916 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6917
6918 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6919}
6920
6921static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6922{
6923 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6924 QgsGeometry geom = feat.geometry();
6925 if ( !geom.isNull() )
6926 return QVariant::fromValue( geom );
6927 return QVariant();
6928}
6929
6930static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6931{
6932 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6933 if ( !feat.isValid() )
6934 return QVariant();
6935 return feat.id();
6936}
6937
6938static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6939{
6940 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6941 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
6942 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6943
6944 if ( !sCrs.isValid() )
6945 return QVariant::fromValue( fGeom );
6946
6947 if ( !dCrs.isValid() )
6948 return QVariant::fromValue( fGeom );
6949
6951 if ( context )
6952 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
6953 QgsCoordinateTransform t( sCrs, dCrs, tContext );
6954 try
6955 {
6957 return QVariant::fromValue( fGeom );
6958 }
6959 catch ( QgsCsException &cse )
6960 {
6961 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
6962 return QVariant();
6963 }
6964 return QVariant();
6965}
6966
6967
6968static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6969{
6970 bool foundLayer = false;
6971 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6972
6973 //no layer found
6974 if ( !featureSource || !foundLayer )
6975 {
6976 return QVariant();
6977 }
6978
6979 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
6980
6982 req.setFilterFid( fid );
6983 req.setTimeout( 10000 );
6984 req.setRequestMayBeNested( true );
6985 if ( context )
6986 req.setFeedback( context->feedback() );
6987 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6988
6989 QgsFeature fet;
6990 QVariant result;
6991 if ( fIt.nextFeature( fet ) )
6992 result = QVariant::fromValue( fet );
6993
6994 return result;
6995}
6996
6997static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6998{
6999 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
7000 bool foundLayer = false;
7001 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7002
7003 //no layer found
7004 if ( !featureSource || !foundLayer )
7005 {
7006 return QVariant();
7007 }
7009 QString cacheValueKey;
7010 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7011 {
7012 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7013
7014 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
7015 QString filterString;
7016 for ( ; i != attributeMap.constEnd(); ++i )
7017 {
7018 if ( !filterString.isEmpty() )
7019 {
7020 filterString.append( " AND " );
7021 }
7022 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
7023 }
7024 cacheValueKey = u"getfeature:%1:%2"_s.arg( featureSource->id(), filterString );
7025 if ( context && context->hasCachedValue( cacheValueKey ) )
7026 {
7027 return context->cachedValue( cacheValueKey );
7028 }
7029 req.setFilterExpression( filterString );
7030 }
7031 else
7032 {
7033 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7034 int attributeId = featureSource->fields().lookupField( attribute );
7035 if ( attributeId == -1 )
7036 {
7037 return QVariant();
7038 }
7039
7040 const QVariant &attVal = values.at( 2 );
7041
7042 cacheValueKey = u"getfeature:%1:%2:%3"_s.arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
7043 if ( context && context->hasCachedValue( cacheValueKey ) )
7044 {
7045 return context->cachedValue( cacheValueKey );
7046 }
7047
7049 }
7050 req.setLimit( 1 );
7051 req.setTimeout( 10000 );
7052 req.setRequestMayBeNested( true );
7053 if ( context )
7054 req.setFeedback( context->feedback() );
7055 if ( !parent->needsGeometry() )
7056 {
7058 }
7059 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7060
7061 QgsFeature fet;
7062 QVariant res;
7063 if ( fIt.nextFeature( fet ) )
7064 {
7065 res = QVariant::fromValue( fet );
7066 }
7067
7068 if ( context )
7069 context->setCachedValue( cacheValueKey, res );
7070 return res;
7071}
7072
7073static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7074{
7075 QVariant result;
7076 QString fieldName;
7077
7078 if ( context )
7079 {
7080 if ( !values.isEmpty() )
7081 {
7082 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
7083 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
7084 fieldName = col->name();
7085 else if ( values.size() == 2 )
7086 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7087 }
7088
7089 QVariant value = values.at( 0 );
7090
7091 const QgsFields fields = context->fields();
7092 int fieldIndex = fields.lookupField( fieldName );
7093
7094 if ( fieldIndex == -1 )
7095 {
7096 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( u"represent_value"_s, fieldName ) );
7097 }
7098 else
7099 {
7100 // TODO this function is NOT thread safe
7102 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
7104
7105 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName, value.toString() );
7106 if ( context->hasCachedValue( cacheValueKey ) )
7107 {
7108 return context->cachedValue( cacheValueKey );
7109 }
7110
7111 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
7113
7114 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName );
7115
7116 QVariant cache;
7117 if ( !context->hasCachedValue( cacheKey ) )
7118 {
7119 cache = formatter->createCache( layer, fieldIndex, setup.config() );
7120 context->setCachedValue( cacheKey, cache );
7121 }
7122 else
7123 cache = context->cachedValue( cacheKey );
7124
7125 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
7126
7127 context->setCachedValue( cacheValueKey, result );
7128 }
7129 }
7130 else
7131 {
7132 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( u"represent_value"_s, fieldName ) );
7133 }
7134
7135 return result;
7136}
7137
7138static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7139{
7140 const QVariant data = values.at( 0 );
7141 const QMimeDatabase db;
7142 return db.mimeTypeForData( data.toByteArray() ).name();
7143}
7144
7145static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7146{
7147 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7148
7149 bool foundLayer = false;
7150 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [layerProperty]( QgsMapLayer * layer )-> QVariant
7151 {
7152 if ( !layer )
7153 return QVariant();
7154
7155 // here, we always prefer the layer metadata values over the older server-specific published values
7156 if ( QString::compare( layerProperty, u"name"_s, Qt::CaseInsensitive ) == 0 )
7157 return layer->name();
7158 else if ( QString::compare( layerProperty, u"id"_s, Qt::CaseInsensitive ) == 0 )
7159 return layer->id();
7160 else if ( QString::compare( layerProperty, u"title"_s, Qt::CaseInsensitive ) == 0 )
7161 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
7162 else if ( QString::compare( layerProperty, u"abstract"_s, Qt::CaseInsensitive ) == 0 )
7163 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
7164 else if ( QString::compare( layerProperty, u"keywords"_s, Qt::CaseInsensitive ) == 0 )
7165 {
7166 QStringList keywords;
7167 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
7168 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
7169 {
7170 keywords.append( it.value() );
7171 }
7172 if ( !keywords.isEmpty() )
7173 return keywords;
7174 return layer->serverProperties()->keywordList();
7175 }
7176 else if ( QString::compare( layerProperty, u"data_url"_s, Qt::CaseInsensitive ) == 0 )
7177 return layer->serverProperties()->dataUrl();
7178 else if ( QString::compare( layerProperty, u"attribution"_s, Qt::CaseInsensitive ) == 0 )
7179 {
7180 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
7181 }
7182 else if ( QString::compare( layerProperty, u"attribution_url"_s, Qt::CaseInsensitive ) == 0 )
7183 return layer->serverProperties()->attributionUrl();
7184 else if ( QString::compare( layerProperty, u"source"_s, Qt::CaseInsensitive ) == 0 )
7185 return layer->publicSource();
7186 else if ( QString::compare( layerProperty, u"min_scale"_s, Qt::CaseInsensitive ) == 0 )
7187 return layer->minimumScale();
7188 else if ( QString::compare( layerProperty, u"max_scale"_s, Qt::CaseInsensitive ) == 0 )
7189 return layer->maximumScale();
7190 else if ( QString::compare( layerProperty, u"is_editable"_s, Qt::CaseInsensitive ) == 0 )
7191 return layer->isEditable();
7192 else if ( QString::compare( layerProperty, u"crs"_s, Qt::CaseInsensitive ) == 0 )
7193 return layer->crs().authid();
7194 else if ( QString::compare( layerProperty, u"crs_definition"_s, Qt::CaseInsensitive ) == 0 )
7195 return layer->crs().toProj();
7196 else if ( QString::compare( layerProperty, u"crs_description"_s, Qt::CaseInsensitive ) == 0 )
7197 return layer->crs().description();
7198 else if ( QString::compare( layerProperty, u"crs_ellipsoid"_s, Qt::CaseInsensitive ) == 0 )
7199 return layer->crs().ellipsoidAcronym();
7200 else if ( QString::compare( layerProperty, u"extent"_s, Qt::CaseInsensitive ) == 0 )
7201 {
7202 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
7203 QVariant result = QVariant::fromValue( extentGeom );
7204 return result;
7205 }
7206 else if ( QString::compare( layerProperty, u"distance_units"_s, Qt::CaseInsensitive ) == 0 )
7207 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
7208 else if ( QString::compare( layerProperty, u"path"_s, Qt::CaseInsensitive ) == 0 )
7209 {
7210 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
7211 return decodedUri.value( u"path"_s );
7212 }
7213 else if ( QString::compare( layerProperty, u"type"_s, Qt::CaseInsensitive ) == 0 )
7214 {
7215 switch ( layer->type() )
7216 {
7218 return QCoreApplication::translate( "expressions", "Vector" );
7220 return QCoreApplication::translate( "expressions", "Raster" );
7222 return QCoreApplication::translate( "expressions", "Mesh" );
7224 return QCoreApplication::translate( "expressions", "Vector Tile" );
7226 return QCoreApplication::translate( "expressions", "Plugin" );
7228 return QCoreApplication::translate( "expressions", "Annotation" );
7230 return QCoreApplication::translate( "expressions", "Point Cloud" );
7232 return QCoreApplication::translate( "expressions", "Group" );
7234 return QCoreApplication::translate( "expressions", "Tiled Scene" );
7235 }
7236 }
7237 else
7238 {
7239 //vector layer methods
7240 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
7241 if ( vLayer )
7242 {
7243 if ( QString::compare( layerProperty, u"storage_type"_s, Qt::CaseInsensitive ) == 0 )
7244 return vLayer->storageType();
7245 else if ( QString::compare( layerProperty, u"geometry_type"_s, Qt::CaseInsensitive ) == 0 )
7247 else if ( QString::compare( layerProperty, u"feature_count"_s, Qt::CaseInsensitive ) == 0 )
7248 return QVariant::fromValue( vLayer->featureCount() );
7249 }
7250 }
7251
7252 return QVariant();
7253 }, foundLayer );
7254
7255 if ( !foundLayer )
7256 return QVariant();
7257 else
7258 return res;
7259}
7260
7261static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7262{
7263 const QString uriPart = values.at( 1 ).toString();
7264
7265 bool foundLayer = false;
7266
7267 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, uriPart]( QgsMapLayer * layer )-> QVariant
7268 {
7269 if ( !layer->dataProvider() )
7270 {
7271 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
7272 return QVariant();
7273 }
7274
7275 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
7276
7277 if ( !uriPart.isNull() )
7278 {
7279 return decodedUri.value( uriPart );
7280 }
7281 else
7282 {
7283 return decodedUri;
7284 }
7285 }, foundLayer );
7286
7287 if ( !foundLayer )
7288 {
7289 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
7290 return QVariant();
7291 }
7292 else
7293 {
7294 return res;
7295 }
7296}
7297
7298static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7299{
7300 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7301 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7302
7303 bool foundLayer = false;
7304 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, band, layerProperty]( QgsMapLayer * layer )-> QVariant
7305 {
7306 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
7307 if ( !rl )
7308 return QVariant();
7309
7310 if ( band < 1 || band > rl->bandCount() )
7311 {
7312 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
7313 return QVariant();
7314 }
7315
7317
7318 if ( QString::compare( layerProperty, u"avg"_s, Qt::CaseInsensitive ) == 0 )
7320 else if ( QString::compare( layerProperty, u"stdev"_s, Qt::CaseInsensitive ) == 0 )
7322 else if ( QString::compare( layerProperty, u"min"_s, Qt::CaseInsensitive ) == 0 )
7324 else if ( QString::compare( layerProperty, u"max"_s, Qt::CaseInsensitive ) == 0 )
7326 else if ( QString::compare( layerProperty, u"range"_s, Qt::CaseInsensitive ) == 0 )
7328 else if ( QString::compare( layerProperty, u"sum"_s, Qt::CaseInsensitive ) == 0 )
7330 else
7331 {
7332 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
7333 return QVariant();
7334 }
7335
7336 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
7337 switch ( stat )
7338 {
7340 return stats.mean;
7342 return stats.stdDev;
7344 return stats.minimumValue;
7346 return stats.maximumValue;
7348 return stats.range;
7350 return stats.sum;
7351 default:
7352 break;
7353 }
7354 return QVariant();
7355 }, foundLayer );
7356
7357 if ( !foundLayer )
7358 {
7359#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
7360 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
7361#endif
7362 return QVariant();
7363 }
7364 else
7365 {
7366 return res;
7367 }
7368}
7369
7370static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7371{
7372 return values;
7373}
7374
7375static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7376{
7377 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7378 bool ascending = values.value( 1 ).toBool();
7379 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
7380 return list;
7381}
7382
7383static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7384{
7385 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
7386}
7387
7388static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7389{
7390 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
7391}
7392
7393static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7394{
7395 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
7396}
7397
7398static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7399{
7400 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7401 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7402 int match = 0;
7403 for ( const auto &item : listB )
7404 {
7405 if ( listA.contains( item ) )
7406 match++;
7407 }
7408
7409 return QVariant( match == listB.count() );
7410}
7411
7412static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7413{
7414 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7415}
7416
7417static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7418{
7419 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7420 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7421 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
7422 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7423 return list.at( list.length() + pos );
7424 return QVariant();
7425}
7426
7427static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7428{
7429 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7430 return list.value( 0 );
7431}
7432
7433static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7434{
7435 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7436 return list.value( list.size() - 1 );
7437}
7438
7439static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7440{
7441 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7442 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7443}
7444
7445static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7446{
7447 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7448 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7449}
7450
7451static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7452{
7453 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7454 int i = 0;
7455 double total = 0.0;
7456 for ( const QVariant &item : list )
7457 {
7458 switch ( item.userType() )
7459 {
7460 case QMetaType::Int:
7461 case QMetaType::UInt:
7462 case QMetaType::LongLong:
7463 case QMetaType::ULongLong:
7464 case QMetaType::Float:
7465 case QMetaType::Double:
7466 total += item.toDouble();
7467 ++i;
7468 break;
7469 }
7470 }
7471 return i == 0 ? QVariant() : total / i;
7472}
7473
7474static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7475{
7476 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7477 QVariantList numbers;
7478 for ( const auto &item : list )
7479 {
7480 switch ( item.userType() )
7481 {
7482 case QMetaType::Int:
7483 case QMetaType::UInt:
7484 case QMetaType::LongLong:
7485 case QMetaType::ULongLong:
7486 case QMetaType::Float:
7487 case QMetaType::Double:
7488 numbers.append( item );
7489 break;
7490 }
7491 }
7492 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7493 const int count = numbers.count();
7494 if ( count == 0 )
7495 {
7496 return QVariant();
7497 }
7498 else if ( count % 2 )
7499 {
7500 return numbers.at( count / 2 );
7501 }
7502 else
7503 {
7504 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7505 }
7506}
7507
7508static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7509{
7510 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7511 int i = 0;
7512 double total = 0.0;
7513 for ( const QVariant &item : list )
7514 {
7515 switch ( item.userType() )
7516 {
7517 case QMetaType::Int:
7518 case QMetaType::UInt:
7519 case QMetaType::LongLong:
7520 case QMetaType::ULongLong:
7521 case QMetaType::Float:
7522 case QMetaType::Double:
7523 total += item.toDouble();
7524 ++i;
7525 break;
7526 }
7527 }
7528 return i == 0 ? QVariant() : total;
7529}
7530
7531static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7532{
7533 QVariant result = value;
7534 ( void )result.convert( static_cast<int>( type ) );
7535 return result;
7536}
7537
7538static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7539{
7540 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7541 QHash< QVariant, int > hash;
7542 for ( const auto &item : list )
7543 {
7544 ++hash[item];
7545 }
7546 const QList< int > occurrences = hash.values();
7547 if ( occurrences.empty() )
7548 return QVariantList();
7549
7550 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7551
7552 const QString option = values.at( 1 ).toString();
7553 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7554 {
7555 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7556 }
7557 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7558 {
7559 if ( hash.isEmpty() )
7560 return QVariant();
7561
7562 return QVariant( hash.key( maxValue ) );
7563 }
7564 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7565 {
7566 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7567 }
7568 else if ( option.compare( "real_majority"_L1, Qt::CaseInsensitive ) == 0 )
7569 {
7570 if ( maxValue * 2 <= list.size() )
7571 return QVariant();
7572
7573 return QVariant( hash.key( maxValue ) );
7574 }
7575 else
7576 {
7577 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7578 return QVariant();
7579 }
7580}
7581
7582static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7583{
7584 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7585 QHash< QVariant, int > hash;
7586 for ( const auto &item : list )
7587 {
7588 ++hash[item];
7589 }
7590 const QList< int > occurrences = hash.values();
7591 if ( occurrences.empty() )
7592 return QVariantList();
7593
7594 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7595
7596 const QString option = values.at( 1 ).toString();
7597 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7598 {
7599 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7600 }
7601 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7602 {
7603 if ( hash.isEmpty() )
7604 return QVariant();
7605
7606 return QVariant( hash.key( minValue ) );
7607 }
7608 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7609 {
7610 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7611 }
7612 else if ( option.compare( "real_minority"_L1, Qt::CaseInsensitive ) == 0 )
7613 {
7614 if ( hash.isEmpty() )
7615 return QVariant();
7616
7617 // Remove the majority, all others are minority
7618 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7619 if ( maxValue * 2 > list.size() )
7620 hash.remove( hash.key( maxValue ) );
7621
7622 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7623 }
7624 else
7625 {
7626 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7627 return QVariant();
7628 }
7629}
7630
7631static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7632{
7633 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7634 list.append( values.at( 1 ) );
7635 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7636}
7637
7638static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7639{
7640 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7641 list.prepend( values.at( 1 ) );
7642 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7643}
7644
7645static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7646{
7647 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7648 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7649 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7650}
7651
7652static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7653{
7654 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7655 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7656 if ( position < 0 )
7657 position = position + list.length();
7658 if ( position >= 0 && position < list.length() )
7659 list.removeAt( position );
7660 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7661}
7662
7663static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7664{
7665 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7666 return QVariant();
7667
7668 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7669
7670 const QVariant toRemove = values.at( 1 );
7671 if ( QgsVariantUtils::isNull( toRemove ) )
7672 {
7673 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant & element )
7674 {
7675 return QgsVariantUtils::isNull( element );
7676 } ), list.end() );
7677 }
7678 else
7679 {
7680 list.removeAll( toRemove );
7681 }
7682 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7683}
7684
7685static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7686{
7687 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7688 {
7689 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7690
7691 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7692 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7693 {
7694 int index = list.indexOf( it.key() );
7695 while ( index >= 0 )
7696 {
7697 list.replace( index, it.value() );
7698 index = list.indexOf( it.key() );
7699 }
7700 }
7701
7702 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7703 }
7704 else if ( values.count() == 3 )
7705 {
7706 QVariantList before;
7707 QVariantList after;
7708 bool isSingleReplacement = false;
7709
7710 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7711 {
7712 before = QVariantList() << values.at( 1 );
7713 }
7714 else
7715 {
7716 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7717 }
7718
7719 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7720 {
7721 after = QVariantList() << values.at( 2 );
7722 isSingleReplacement = true;
7723 }
7724 else
7725 {
7726 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7727 }
7728
7729 if ( !isSingleReplacement && before.length() != after.length() )
7730 {
7731 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7732 return QVariant();
7733 }
7734
7735 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7736 for ( int i = 0; i < before.length(); i++ )
7737 {
7738 int index = list.indexOf( before.at( i ) );
7739 while ( index >= 0 )
7740 {
7741 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7742 index = list.indexOf( before.at( i ) );
7743 }
7744 }
7745
7746 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7747 }
7748 else
7749 {
7750 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7751 return QVariant();
7752 }
7753}
7754
7755static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7756{
7757 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7758 QVariantList list_new;
7759
7760 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7761 {
7762 while ( list.removeOne( cur ) )
7763 {
7764 list_new.append( cur );
7765 }
7766 }
7767
7768 list_new.append( list );
7769
7770 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7771}
7772
7773static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7774{
7775 QVariantList list;
7776 for ( const QVariant &cur : values )
7777 {
7778 list += QgsExpressionUtils::getListValue( cur, parent );
7779 }
7780 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7781}
7782
7783static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7784{
7785 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7786 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7787 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7788 int slice_length = 0;
7789 // negative positions means positions taken relative to the end of the array
7790 if ( start_pos < 0 )
7791 {
7792 start_pos = list.length() + start_pos;
7793 }
7794 if ( end_pos >= 0 )
7795 {
7796 slice_length = end_pos - start_pos + 1;
7797 }
7798 else
7799 {
7800 slice_length = list.length() + end_pos - start_pos + 1;
7801 }
7802 //avoid negative lengths in QList.mid function
7803 if ( slice_length < 0 )
7804 {
7805 slice_length = 0;
7806 }
7807 list = list.mid( start_pos, slice_length );
7808 return list;
7809}
7810
7811static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7812{
7813 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7814 std::reverse( list.begin(), list.end() );
7815 return list;
7816}
7817
7818static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7819{
7820 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7821 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7822 for ( const QVariant &cur : array2 )
7823 {
7824 if ( array1.contains( cur ) )
7825 return QVariant( true );
7826 }
7827 return QVariant( false );
7828}
7829
7830static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7831{
7832 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7833
7834 QVariantList distinct;
7835
7836 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7837 {
7838 if ( !distinct.contains( *it ) )
7839 {
7840 distinct += ( *it );
7841 }
7842 }
7843
7844 return distinct;
7845}
7846
7847static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7848{
7849 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7850 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7851 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7852
7853 QString str;
7854
7855 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7856 {
7857 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
7858 if ( it != ( array.constEnd() - 1 ) )
7859 {
7860 str += delimiter;
7861 }
7862 }
7863
7864 return QVariant( str );
7865}
7866
7867static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7868{
7869 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7870 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7871 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7872
7873 QStringList list = str.split( delimiter );
7874 QVariantList array;
7875
7876 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
7877 {
7878 array += ( !( *it ).isEmpty() ) ? *it : empty;
7879 }
7880
7881 return array;
7882}
7883
7884static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7885{
7886 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7887 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
7888 if ( document.isNull() )
7889 return QVariant();
7890
7891 return document.toVariant();
7892}
7893
7894static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7895{
7896 Q_UNUSED( parent )
7897 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
7898 return QString( document.toJson( QJsonDocument::Compact ) );
7899}
7900
7901static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7902{
7903 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7904 if ( str.isEmpty() )
7905 return QVariantMap();
7906 str = str.trimmed();
7907
7908 return QgsHstoreUtils::parse( str );
7909}
7910
7911static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7912{
7913 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7914 return QgsHstoreUtils::build( map );
7915}
7916
7917static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7918{
7919 QVariantMap result;
7920 for ( int i = 0; i + 1 < values.length(); i += 2 )
7921 {
7922 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
7923 }
7924 return result;
7925}
7926
7927static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7928{
7929 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7930 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7931 QVariantMap resultMap;
7932
7933 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7934 {
7935 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
7936 }
7937
7938 return resultMap;
7939}
7940
7941static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7942{
7943 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
7944}
7945
7946static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7947{
7948 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
7949}
7950
7951static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7952{
7953 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7954 map.remove( values.at( 1 ).toString() );
7955 return map;
7956}
7957
7958static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7959{
7960 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7961 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
7962 return map;
7963}
7964
7965static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7966{
7967 QVariantMap result;
7968 for ( const QVariant &cur : values )
7969 {
7970 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
7971 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
7972 result.insert( it.key(), it.value() );
7973 }
7974 return result;
7975}
7976
7977static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7978{
7979 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
7980}
7981
7982static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7983{
7984 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
7985}
7986
7987static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7988{
7989 const QString envVarName = values.at( 0 ).toString();
7990 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
7991 return QVariant();
7992
7993 return QProcessEnvironment::systemEnvironment().value( envVarName );
7994}
7995
7996static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7997{
7998 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7999 if ( parent->hasEvalError() )
8000 {
8001 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "base_file_name"_L1 ) );
8002 return QVariant();
8003 }
8004 return QFileInfo( file ).completeBaseName();
8005}
8006
8007static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8008{
8009 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8010 if ( parent->hasEvalError() )
8011 {
8012 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_suffix"_L1 ) );
8013 return QVariant();
8014 }
8015 return QFileInfo( file ).completeSuffix();
8016}
8017
8018static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8019{
8020 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8021 if ( parent->hasEvalError() )
8022 {
8023 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_exists"_L1 ) );
8024 return QVariant();
8025 }
8026 return QFileInfo::exists( file );
8027}
8028
8029static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8030{
8031 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8032 if ( parent->hasEvalError() )
8033 {
8034 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_name"_L1 ) );
8035 return QVariant();
8036 }
8037 return QFileInfo( file ).fileName();
8038}
8039
8040static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8041{
8042 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8043 if ( parent->hasEvalError() )
8044 {
8045 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_file"_L1 ) );
8046 return QVariant();
8047 }
8048 return QFileInfo( file ).isFile();
8049}
8050
8051static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8052{
8053 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8054 if ( parent->hasEvalError() )
8055 {
8056 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_directory"_L1 ) );
8057 return QVariant();
8058 }
8059 return QFileInfo( file ).isDir();
8060}
8061
8062static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8063{
8064 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8065 if ( parent->hasEvalError() )
8066 {
8067 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_path"_L1 ) );
8068 return QVariant();
8069 }
8070 return QDir::toNativeSeparators( QFileInfo( file ).path() );
8071}
8072
8073static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8074{
8075 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8076 if ( parent->hasEvalError() )
8077 {
8078 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_size"_L1 ) );
8079 return QVariant();
8080 }
8081 return QFileInfo( file ).size();
8082}
8083
8084static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
8085{
8086 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
8087}
8088
8089static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8090{
8091 QVariant hash;
8092 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8093 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
8094
8095 if ( method == "md4"_L1 )
8096 {
8097 hash = fcnHash( str, QCryptographicHash::Md4 );
8098 }
8099 else if ( method == "md5"_L1 )
8100 {
8101 hash = fcnHash( str, QCryptographicHash::Md5 );
8102 }
8103 else if ( method == "sha1"_L1 )
8104 {
8105 hash = fcnHash( str, QCryptographicHash::Sha1 );
8106 }
8107 else if ( method == "sha224"_L1 )
8108 {
8109 hash = fcnHash( str, QCryptographicHash::Sha224 );
8110 }
8111 else if ( method == "sha256"_L1 )
8112 {
8113 hash = fcnHash( str, QCryptographicHash::Sha256 );
8114 }
8115 else if ( method == "sha384"_L1 )
8116 {
8117 hash = fcnHash( str, QCryptographicHash::Sha384 );
8118 }
8119 else if ( method == "sha512"_L1 )
8120 {
8121 hash = fcnHash( str, QCryptographicHash::Sha512 );
8122 }
8123 else if ( method == "sha3_224"_L1 )
8124 {
8125 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
8126 }
8127 else if ( method == "sha3_256"_L1 )
8128 {
8129 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
8130 }
8131 else if ( method == "sha3_384"_L1 )
8132 {
8133 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
8134 }
8135 else if ( method == "sha3_512"_L1 )
8136 {
8137 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
8138 }
8139 else if ( method == "keccak_224"_L1 )
8140 {
8141 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
8142 }
8143 else if ( method == "keccak_256"_L1 )
8144 {
8145 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
8146 }
8147 else if ( method == "keccak_384"_L1 )
8148 {
8149 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
8150 }
8151 else if ( method == "keccak_512"_L1 )
8152 {
8153 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
8154 }
8155 else
8156 {
8157 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
8158 }
8159 return hash;
8160}
8161
8162static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8163{
8164 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
8165}
8166
8167static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8168{
8169 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
8170}
8171
8172static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8173{
8174 const QByteArray input = values.at( 0 ).toByteArray();
8175 return QVariant( QString( input.toBase64() ) );
8176}
8177
8178static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8179{
8180 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8181 QUrlQuery query;
8182 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8183 {
8184 query.addQueryItem( it.key(), it.value().toString() );
8185 }
8186 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
8187}
8188
8189static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8190{
8191 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8192 const QByteArray base64 = value.toLocal8Bit();
8193 const QByteArray decoded = QByteArray::fromBase64( base64 );
8194 return QVariant( decoded );
8195}
8196
8197typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
8198
8199static 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 )
8200{
8201
8202 if ( ! context )
8203 {
8204 parent->setEvalErrorString( u"This function was called without an expression context."_s );
8205 return QVariant();
8206 }
8207
8208 const QVariant sourceLayerRef = context->variable( u"layer"_s ); //used to detect if sourceLayer and targetLayer are the same
8209 // TODO this function is NOT thread safe
8211 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
8213
8214 QgsFeatureRequest request;
8215 request.setTimeout( 10000 );
8216 request.setRequestMayBeNested( true );
8217 request.setFeedback( context->feedback() );
8218
8219 // First parameter is the overlay layer
8220 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
8222
8223 const bool layerCanBeCached = node->isStatic( parent, context );
8224 QVariant targetLayerValue = node->eval( parent, context );
8226
8227 // Second parameter is the expression to evaluate (or null for testonly)
8228 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
8230 QString subExpString = node->dump();
8231
8232 bool testOnly = ( subExpString == "NULL" );
8233 // TODO this function is NOT thread safe
8235 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
8237 if ( !targetLayer ) // No layer, no joy
8238 {
8239 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
8240 return QVariant();
8241 }
8242
8243 // Third parameter is the filtering expression
8244 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
8246 QString filterString = node->dump();
8247 if ( filterString != "NULL" )
8248 {
8249 request.setFilterExpression( filterString ); //filter cached features
8250 }
8251
8252 // Fourth parameter is the limit
8253 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8255 QVariant limitValue = node->eval( parent, context );
8257 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
8258
8259 // Fifth parameter (for nearest only) is the max distance
8260 double max_distance = 0;
8261 if ( isNearestFunc ) //maxdistance param handling
8262 {
8263 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8265 QVariant distanceValue = node->eval( parent, context );
8267 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
8268 }
8269
8270 // Fifth or sixth (for nearest only) parameter is the cache toggle
8271 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
8273 QVariant cacheValue = node->eval( parent, context );
8275 bool cacheEnabled = cacheValue.toBool();
8276
8277 // Sixth parameter (for intersects only) is the min overlap (area or length)
8278 // Seventh parameter (for intersects only) is the min inscribed circle radius
8279 // Eighth parameter (for intersects only) is the return_details
8280 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
8281 double minOverlap { -1 };
8282 double minInscribedCircleRadius { -1 };
8283 bool returnDetails = false; //#spellok
8284 bool sortByMeasure = false;
8285 bool sortAscending = false;
8286 bool requireMeasures = false;
8287 bool overlapOrRadiusFilter = false;
8288 if ( isIntersectsFunc )
8289 {
8290
8291 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8293 const QVariant minOverlapValue = node->eval( parent, context );
8295 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
8296 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8298 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
8300 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
8301 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
8302 // Return measures is only effective when an expression is set
8303 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
8304 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
8305 // Sort by measures is only effective when an expression is set
8306 const QString sorting { node->eval( parent, context ).toString().toLower() };
8307 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
8308 sortAscending = sorting.startsWith( "asc" );
8309 requireMeasures = sortByMeasure || returnDetails; //#spellok
8310 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
8311 }
8312
8313
8314 FEAT_FROM_CONTEXT( context, feat )
8315 const QgsGeometry geometry = feat.geometry();
8316
8317 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
8318 {
8319 QgsCoordinateTransformContext TransformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
8320 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
8321 }
8322
8323 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
8324
8325 QgsRectangle intDomain = geometry.boundingBox();
8326 if ( bboxGrow != 0 )
8327 {
8328 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
8329 }
8330
8331 const QString cacheBase { u"%1:%2:%3"_s.arg( targetLayer->id(), subExpString, filterString ) };
8332
8333 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
8334 // Otherwise, it can be toggled by the user
8335 QgsSpatialIndex spatialIndex;
8336 QgsVectorLayer *cachedTarget;
8337 QList<QgsFeature> features;
8338 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
8339 {
8340 // If the cache (local spatial index) is enabled, we materialize the whole
8341 // layer, then do the request on that layer instead.
8342 const QString cacheLayer { u"ovrlaylyr:%1"_s.arg( cacheBase ) };
8343 const QString cacheIndex { u"ovrlayidx:%1"_s.arg( cacheBase ) };
8344
8345 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
8346 {
8347 cachedTarget = targetLayer->materialize( request );
8348 if ( layerCanBeCached )
8349 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
8350 }
8351 else
8352 {
8353 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
8354 }
8355
8356 if ( !context->hasCachedValue( cacheIndex ) )
8357 {
8358 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
8359 if ( layerCanBeCached )
8360 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
8361 }
8362 else
8363 {
8364 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
8365 }
8366
8367 QList<QgsFeatureId> fidsList;
8368 if ( isNearestFunc )
8369 {
8370 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
8371 }
8372 else
8373 {
8374 fidsList = spatialIndex.intersects( intDomain );
8375 }
8376
8377 QListIterator<QgsFeatureId> i( fidsList );
8378 while ( i.hasNext() )
8379 {
8380 QgsFeatureId fId2 = i.next();
8381 if ( sameLayers && feat.id() == fId2 )
8382 continue;
8383 features.append( cachedTarget->getFeature( fId2 ) );
8384 }
8385
8386 }
8387 else
8388 {
8389 // If the cache (local spatial index) is not enabled, we directly
8390 // get the features from the target layer
8391 request.setFilterRect( intDomain );
8392 QgsFeatureIterator fit = targetLayer->getFeatures( request );
8393 QgsFeature feat2;
8394 while ( fit.nextFeature( feat2 ) )
8395 {
8396 if ( sameLayers && feat.id() == feat2.id() )
8397 continue;
8398 features.append( feat2 );
8399 }
8400 }
8401
8402 QgsExpression subExpression;
8403 QgsExpressionContext subContext;
8404 if ( !testOnly )
8405 {
8406 const QString expCacheKey { u"exp:%1"_s.arg( cacheBase ) };
8407 const QString ctxCacheKey { u"ctx:%1"_s.arg( cacheBase ) };
8408
8409 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
8410 {
8411 subExpression = QgsExpression( subExpString );
8413 subExpression.prepare( &subContext );
8414 }
8415 else
8416 {
8417 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8418 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8419 }
8420 }
8421
8422 // //////////////////////////////////////////////////////////////////
8423 // Helper functions for geometry tests
8424
8425 // Test function for linestring geometries, returns TRUE if test passes
8426 auto testLinestring = [minOverlap, requireMeasures]( const QgsGeometry intersection, double & overlapValue ) -> bool
8427 {
8428 bool testResult { false };
8429 // For return measures:
8430 QVector<double> overlapValues;
8431 const QgsGeometry merged { intersection.mergeLines() };
8432 for ( auto it = merged.const_parts_begin(); ! testResult && it != merged.const_parts_end(); ++it )
8433 {
8435 // Check min overlap for intersection (if set)
8436 if ( minOverlap != -1 || requireMeasures )
8437 {
8438 overlapValue = geom->length();
8439 overlapValues.append( overlapValue );
8440 if ( minOverlap != -1 )
8441 {
8442 if ( overlapValue >= minOverlap )
8443 {
8444 testResult = true;
8445 }
8446 else
8447 {
8448 continue;
8449 }
8450 }
8451 }
8452 }
8453
8454 if ( ! overlapValues.isEmpty() )
8455 {
8456 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8457 }
8458
8459 return testResult;
8460 };
8461
8462 // Test function for polygon geometries, returns TRUE if test passes
8463 auto testPolygon = [minOverlap, requireMeasures, minInscribedCircleRadius]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
8464 {
8465 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8466 bool testResult { false };
8467 // For return measures:
8468 QVector<double> overlapValues;
8469 QVector<double> radiusValues;
8470 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8471 {
8473 // Check min overlap for intersection (if set)
8474 if ( minOverlap != -1 || requireMeasures )
8475 {
8476 overlapValue = geom->area();
8477 overlapValues.append( geom->area() );
8478 if ( minOverlap != - 1 )
8479 {
8480 if ( overlapValue >= minOverlap )
8481 {
8482 testResult = true;
8483 }
8484 else
8485 {
8486 continue;
8487 }
8488 }
8489 }
8490
8491 // Check min inscribed circle radius for intersection (if set)
8492 if ( minInscribedCircleRadius != -1 || requireMeasures )
8493 {
8494 const QgsRectangle bbox = geom->boundingBox();
8495 const double width = bbox.width();
8496 const double height = bbox.height();
8497 const double size = width > height ? width : height;
8498 const double tolerance = size / 100.0;
8499 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8500 testResult = radiusValue >= minInscribedCircleRadius;
8501 radiusValues.append( radiusValues );
8502 }
8503 } // end for parts
8504
8505 // Get the max values
8506 if ( !radiusValues.isEmpty() )
8507 {
8508 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8509 }
8510
8511 if ( ! overlapValues.isEmpty() )
8512 {
8513 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8514 }
8515
8516 return testResult;
8517
8518 };
8519
8520
8521 bool found = false;
8522 int foundCount = 0;
8523 QVariantList results;
8524
8525 QListIterator<QgsFeature> i( features );
8526 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8527 {
8528
8529 QgsFeature feat2 = i.next();
8530
8531
8532 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8533 {
8534
8535 double overlapValue = -1;
8536 double radiusValue = -1;
8537
8538 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8539 {
8540
8541 QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
8542
8543 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8544 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8545 {
8546 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8547 intersection = QgsGeometry();
8548 QgsMultiPolygonXY poly;
8549 QgsMultiPolylineXY line;
8550 QgsMultiPointXY point;
8551 for ( const auto &geom : std::as_const( geometries ) )
8552 {
8553 switch ( geom.type() )
8554 {
8556 {
8557 poly.append( geom.asPolygon() );
8558 break;
8559 }
8561 {
8562 line.append( geom.asPolyline() );
8563 break;
8564 }
8566 {
8567 point.append( geom.asPoint() );
8568 break;
8569 }
8572 {
8573 break;
8574 }
8575 }
8576 }
8577
8578 switch ( geometry.type() )
8579 {
8581 {
8582 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8583 break;
8584 }
8586 {
8587 intersection = QgsGeometry::fromMultiPolylineXY( line );
8588 break;
8589 }
8591 {
8592 intersection = QgsGeometry::fromMultiPointXY( point );
8593 break;
8594 }
8597 {
8598 break;
8599 }
8600 }
8601 }
8602
8603 // Depending on the intersection geometry type and on the geometry type of
8604 // the tested geometry we can run different tests and collect different measures
8605 // that can be used for sorting (if required).
8606 switch ( intersection.type() )
8607 {
8608
8610 {
8611
8612 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8613 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8614
8615 if ( ! testResult && overlapOrRadiusFilter )
8616 {
8617 continue;
8618 }
8619
8620 break;
8621 }
8622
8624 {
8625
8626 // If the intersection is a linestring and a minimum circle is required
8627 // we can discard this result immediately.
8628 if ( minInscribedCircleRadius != -1 )
8629 {
8630 continue;
8631 }
8632
8633 // Otherwise a test for the overlap value is performed.
8634 const bool testResult { testLinestring( intersection, overlapValue ) };
8635
8636 if ( ! testResult && overlapOrRadiusFilter )
8637 {
8638 continue;
8639 }
8640
8641 break;
8642 }
8643
8645 {
8646
8647 // If the intersection is a point and a minimum circle is required
8648 // we can discard this result immediately.
8649 if ( minInscribedCircleRadius != -1 )
8650 {
8651 continue;
8652 }
8653
8654 bool testResult { false };
8655 if ( minOverlap != -1 || requireMeasures )
8656 {
8657 // Initially set this to 0 because it's a point intersection...
8658 overlapValue = 0;
8659 // ... but if the target geometry is not a point and the source
8660 // geometry is a point, we must record the length or the area
8661 // of the intersected geometry and use that as a measure for
8662 // sorting or reporting.
8663 if ( geometry.type() == Qgis::GeometryType::Point )
8664 {
8665 switch ( feat2.geometry().type() )
8666 {
8670 {
8671 break;
8672 }
8674 {
8675 testResult = testLinestring( feat2.geometry(), overlapValue );
8676 break;
8677 }
8679 {
8680 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8681 break;
8682 }
8683 }
8684 }
8685
8686 if ( ! testResult && overlapOrRadiusFilter )
8687 {
8688 continue;
8689 }
8690
8691 }
8692 break;
8693 }
8694
8697 {
8698 continue;
8699 }
8700 }
8701 }
8702
8703 found = true;
8704 foundCount++;
8705
8706 // We just want a single boolean result if there is any intersect: finish and return true
8707 if ( testOnly )
8708 break;
8709
8710 if ( !invert )
8711 {
8712 // We want a list of attributes / geometries / other expression values, evaluate now
8713 subContext.setFeature( feat2 );
8714 const QVariant expResult = subExpression.evaluate( &subContext );
8715
8716 if ( requireMeasures )
8717 {
8718 QVariantMap resultRecord;
8719 resultRecord.insert( u"id"_s, feat2.id() );
8720 resultRecord.insert( u"result"_s, expResult );
8721 // Overlap is always added because return measures was set
8722 resultRecord.insert( u"overlap"_s, overlapValue );
8723 // Radius is only added when is different than -1 (because for linestrings is not set)
8724 if ( radiusValue != -1 )
8725 {
8726 resultRecord.insert( u"radius"_s, radiusValue );
8727 }
8728 results.append( resultRecord );
8729 }
8730 else
8731 {
8732 results.append( expResult );
8733 }
8734 }
8735 else
8736 {
8737 // If not, results is a list of found ids, which we'll inverse and evaluate below
8738 results.append( feat2.id() );
8739 }
8740 }
8741 }
8742
8743 if ( testOnly )
8744 {
8745 if ( invert )
8746 found = !found;//for disjoint condition
8747 return found;
8748 }
8749
8750 if ( !invert )
8751 {
8752 if ( requireMeasures )
8753 {
8754 if ( sortByMeasure )
8755 {
8756 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
8757 {
8758 return sortAscending ?
8759 recordB.toMap().value( u"overlap"_s ).toDouble() > recordA.toMap().value( u"overlap"_s ).toDouble()
8760 : recordA.toMap().value( u"overlap"_s ).toDouble() > recordB.toMap().value( u"overlap"_s ).toDouble();
8761 } );
8762 }
8763 // Resize
8764 if ( limit > 0 && results.size() > limit )
8765 {
8766 results.erase( results.begin() + limit );
8767 }
8768
8769 if ( ! returnDetails ) //#spellok
8770 {
8771 QVariantList expResults;
8772 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8773 {
8774 expResults.append( it->toMap().value( u"result"_s ) );
8775 }
8776 return expResults;
8777 }
8778 }
8779
8780 return results;
8781 }
8782
8783 // for disjoint condition returns the results for cached layers not intersected feats
8784 QVariantList disjoint_results;
8785 QgsFeature feat2;
8786 QgsFeatureRequest request2;
8787 request2.setLimit( limit );
8788 if ( context )
8789 request2.setFeedback( context->feedback() );
8790 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
8791 while ( fi.nextFeature( feat2 ) )
8792 {
8793 if ( !results.contains( feat2.id() ) )
8794 {
8795 subContext.setFeature( feat2 );
8796 disjoint_results.append( subExpression.evaluate( &subContext ) );
8797 }
8798 }
8799 return disjoint_results;
8800
8801}
8802
8803// Intersect functions:
8804
8805static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8806{
8807 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
8808}
8809
8810static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8811{
8812 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
8813}
8814
8815static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8816{
8817 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
8818}
8819
8820static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8821{
8822 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
8823}
8824
8825static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8826{
8827 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
8828}
8829
8830static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8831{
8832 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
8833}
8835static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8836{
8837 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
8838}
8839
8840static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8841{
8842 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
8843}
8844
8845const QList<QgsExpressionFunction *> &QgsExpression::Functions()
8846{
8847 // The construction of the list isn't thread-safe, and without the mutex,
8848 // crashes in the WFS provider may occur, since it can parse expressions
8849 // in parallel.
8850 // The mutex needs to be recursive.
8851 QMutexLocker locker( &sFunctionsMutex );
8852
8853 QList<QgsExpressionFunction *> &functions = *sFunctions();
8854
8855 if ( functions.isEmpty() )
8856 {
8858 << QgsExpressionFunction::Parameter( u"expression"_s )
8859 << QgsExpressionFunction::Parameter( u"group_by"_s, true )
8860 << QgsExpressionFunction::Parameter( u"filter"_s, true );
8861
8862 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
8863 aggParamsConcat << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
8864 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
8865
8866 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
8867 aggParamsArray << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
8868
8869 functions
8870 << new QgsStaticExpressionFunction( u"sqrt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnSqrt, u"Math"_s )
8871 << new QgsStaticExpressionFunction( u"radians"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"degrees"_s ), fcnRadians, u"Math"_s )
8872 << new QgsStaticExpressionFunction( u"degrees"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"radians"_s ), fcnDegrees, u"Math"_s )
8873 << new QgsStaticExpressionFunction( u"azimuth"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point_a"_s ) << QgsExpressionFunction::Parameter( u"point_b"_s ), fcnAzimuth, u"GeometryGroup"_s )
8874 << new QgsStaticExpressionFunction( u"bearing"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point_a"_s ) << QgsExpressionFunction::Parameter( u"point_b"_s ) << QgsExpressionFunction::Parameter( u"source_crs"_s, true, QVariant() ) << QgsExpressionFunction::Parameter( u"ellipsoid"_s, true, QVariant() ), fcnBearing, u"GeometryGroup"_s )
8875 << new QgsStaticExpressionFunction( u"inclination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point_a"_s ) << QgsExpressionFunction::Parameter( u"point_b"_s ), fcnInclination, u"GeometryGroup"_s )
8876 << new QgsStaticExpressionFunction( u"project"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ) << QgsExpressionFunction::Parameter( u"azimuth"_s ) << QgsExpressionFunction::Parameter( u"elevation"_s, true, M_PI_2 ), fcnProject, u"GeometryGroup"_s )
8877 << new QgsStaticExpressionFunction( u"abs"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAbs, u"Math"_s )
8878 << new QgsStaticExpressionFunction( u"cos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnCos, u"Math"_s )
8879 << new QgsStaticExpressionFunction( u"sin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnSin, u"Math"_s )
8880 << new QgsStaticExpressionFunction( u"tan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnTan, u"Math"_s )
8881 << new QgsStaticExpressionFunction( u"asin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAsin, u"Math"_s )
8882 << new QgsStaticExpressionFunction( u"acos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAcos, u"Math"_s )
8883 << new QgsStaticExpressionFunction( u"atan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAtan, u"Math"_s )
8884 << new QgsStaticExpressionFunction( u"atan2"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"dx"_s ) << QgsExpressionFunction::Parameter( u"dy"_s ), fcnAtan2, u"Math"_s )
8885 << new QgsStaticExpressionFunction( u"exp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnExp, u"Math"_s )
8886 << new QgsStaticExpressionFunction( u"ln"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLn, u"Math"_s )
8887 << new QgsStaticExpressionFunction( u"log10"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog10, u"Math"_s )
8888 << new QgsStaticExpressionFunction( u"log"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"base"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog, u"Math"_s )
8889 << new QgsStaticExpressionFunction( u"round"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"places"_s, true, 0 ), fcnRound, u"Math"_s );
8890
8891 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( u"rand"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s ) << QgsExpressionFunction::Parameter( u"max"_s ) << QgsExpressionFunction::Parameter( u"seed"_s, true ), fcnRnd, u"Math"_s );
8892 randFunc->setIsStatic( false );
8893 functions << randFunc;
8894
8895 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction( u"randf"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s, true, 0.0 ) << QgsExpressionFunction::Parameter( u"max"_s, true, 1.0 ) << QgsExpressionFunction::Parameter( u"seed"_s, true ), fcnRndF, u"Math"_s );
8896 randfFunc->setIsStatic( false );
8897 functions << randfFunc;
8898
8899 functions
8900 << new QgsStaticExpressionFunction( u"max"_s, -1, fcnMax, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8901 << new QgsStaticExpressionFunction( u"min"_s, -1, fcnMin, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8902 << new QgsStaticExpressionFunction( u"clamp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s ) << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"max"_s ), fcnClamp, u"Math"_s )
8903 << new QgsStaticExpressionFunction( u"scale_linear"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"domain_min"_s ) << QgsExpressionFunction::Parameter( u"domain_max"_s ) << QgsExpressionFunction::Parameter( u"range_min"_s ) << QgsExpressionFunction::Parameter( u"range_max"_s ), fcnLinearScale, u"Math"_s )
8904 << new QgsStaticExpressionFunction( u"scale_polynomial"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"domain_min"_s ) << QgsExpressionFunction::Parameter( u"domain_max"_s ) << QgsExpressionFunction::Parameter( u"range_min"_s ) << QgsExpressionFunction::Parameter( u"range_max"_s ) << QgsExpressionFunction::Parameter( u"exponent"_s ), fcnPolynomialScale, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList() << u"scale_exp"_s )
8905 << new QgsStaticExpressionFunction( u"scale_exponential"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"domain_min"_s ) << QgsExpressionFunction::Parameter( u"domain_max"_s ) << QgsExpressionFunction::Parameter( u"range_min"_s ) << QgsExpressionFunction::Parameter( u"range_max"_s ) << QgsExpressionFunction::Parameter( u"exponent"_s ), fcnExponentialScale, u"Math"_s )
8906 << new QgsStaticExpressionFunction( u"floor"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnFloor, u"Math"_s )
8907 << new QgsStaticExpressionFunction( u"ceil"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnCeil, u"Math"_s )
8908 << new QgsStaticExpressionFunction( u"pi"_s, 0, fcnPi, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$pi"_s )
8909 << new QgsStaticExpressionFunction( u"to_bool"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToBool, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"tobool"_s, /* handlesNull = */ true )
8910 << new QgsStaticExpressionFunction( u"to_int"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToInt, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"toint"_s )
8911 << new QgsStaticExpressionFunction( u"to_real"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToReal, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"toreal"_s )
8912 << new QgsStaticExpressionFunction( u"to_string"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToString, QStringList() << u"Conversions"_s << u"String"_s, QString(), false, QSet<QString>(), false, QStringList() << u"tostring"_s )
8913 << new QgsStaticExpressionFunction( u"to_datetime"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() ) << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ), fcnToDateTime, QStringList() << u"Conversions"_s << u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"todatetime"_s )
8914 << new QgsStaticExpressionFunction( u"to_date"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() ) << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ), fcnToDate, QStringList() << u"Conversions"_s << u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"todate"_s )
8915 << new QgsStaticExpressionFunction( u"to_time"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() ) << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ), fcnToTime, QStringList() << u"Conversions"_s << u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"totime"_s )
8916 << new QgsStaticExpressionFunction( u"to_interval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToInterval, QStringList() << u"Conversions"_s << u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"tointerval"_s )
8917 << new QgsStaticExpressionFunction( u"to_dm"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"axis"_s ) << QgsExpressionFunction::Parameter( u"precision"_s ) << QgsExpressionFunction::Parameter( u"formatting"_s, true ), fcnToDegreeMinute, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"todm"_s )
8918 << new QgsStaticExpressionFunction( u"to_dms"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"axis"_s ) << QgsExpressionFunction::Parameter( u"precision"_s ) << QgsExpressionFunction::Parameter( u"formatting"_s, true ), fcnToDegreeMinuteSecond, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"todms"_s )
8919 << new QgsStaticExpressionFunction( u"to_decimal"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToDecimal, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"todecimal"_s )
8920 << new QgsStaticExpressionFunction( u"extract_degrees"_s, { QgsExpressionFunction::Parameter{ u"value"_s } }, fcnExtractDegrees, u"Conversions"_s )
8921 << new QgsStaticExpressionFunction( u"extract_minutes"_s, { QgsExpressionFunction::Parameter{ u"value"_s } }, fcnExtractMinutes, u"Conversions"_s )
8922 << new QgsStaticExpressionFunction( u"extract_seconds"_s, { QgsExpressionFunction::Parameter{ u"value"_s } }, fcnExtractSeconds, u"Conversions"_s )
8923 << new QgsStaticExpressionFunction( u"coalesce"_s, -1, fcnCoalesce, u"Conditionals"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
8924 << new QgsStaticExpressionFunction( u"nullif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value1"_s ) << QgsExpressionFunction::Parameter( u"value2"_s ), fcnNullIf, u"Conditionals"_s )
8925 << new QgsStaticExpressionFunction( u"if"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"condition"_s ) << QgsExpressionFunction::Parameter( u"result_when_true"_s ) << QgsExpressionFunction::Parameter( u"result_when_false"_s ), fcnIf, u"Conditionals"_s, QString(), false, QSet<QString>(), true )
8926 << new QgsStaticExpressionFunction( u"try"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ) << QgsExpressionFunction::Parameter( u"alternative"_s, true, QVariant() ), fcnTry, u"Conditionals"_s, QString(), false, QSet<QString>(), true )
8927
8928 << new QgsStaticExpressionFunction( u"aggregate"_s,
8930 << QgsExpressionFunction::Parameter( u"layer"_s )
8931 << QgsExpressionFunction::Parameter( u"aggregate"_s )
8932 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
8933 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
8934 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
8935 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
8936 fcnAggregate,
8937 u"Aggregates"_s,
8938 QString(),
8939 []( const QgsExpressionNodeFunction * node )
8940 {
8941 // usesGeometry callback: return true if @parent variable is referenced
8942
8943 if ( !node )
8944 return true;
8945
8946 if ( !node->args() )
8947 return false;
8948
8949 QSet<QString> referencedVars;
8950 if ( node->args()->count() > 2 )
8951 {
8952 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8953 referencedVars = subExpressionNode->referencedVariables();
8954 }
8955
8956 if ( node->args()->count() > 3 )
8957 {
8958 QgsExpressionNode *filterNode = node->args()->at( 3 );
8959 referencedVars.unite( filterNode->referencedVariables() );
8960 }
8961 return referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() );
8962 },
8963 []( const QgsExpressionNodeFunction * node )
8964 {
8965 // referencedColumns callback: return AllAttributes if @parent variable is referenced
8966
8967 if ( !node )
8968 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8969
8970 if ( !node->args() )
8971 return QSet<QString>();
8972
8973 QSet<QString> referencedCols;
8974 QSet<QString> referencedVars;
8975
8976 if ( node->args()->count() > 2 )
8977 {
8978 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8979 referencedVars = subExpressionNode->referencedVariables();
8980 referencedCols = subExpressionNode->referencedColumns();
8981 }
8982 if ( node->args()->count() > 3 )
8983 {
8984 QgsExpressionNode *filterNode = node->args()->at( 3 );
8985 referencedVars = filterNode->referencedVariables();
8986 referencedCols.unite( filterNode->referencedColumns() );
8987 }
8988
8989 if ( referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() ) )
8990 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8991 else
8992 return referencedCols;
8993 },
8994 true
8995 )
8996
8997 << new QgsStaticExpressionFunction( u"relation_aggregate"_s, QgsExpressionFunction::ParameterList()
8998 << QgsExpressionFunction::Parameter( u"relation"_s )
8999 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9000 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9001 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9002 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9003 fcnAggregateRelation, u"Aggregates"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
9004
9005 << new QgsStaticExpressionFunction( u"count"_s, aggParams, fcnAggregateCount, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9006 << new QgsStaticExpressionFunction( u"count_distinct"_s, aggParams, fcnAggregateCountDistinct, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9007 << new QgsStaticExpressionFunction( u"count_missing"_s, aggParams, fcnAggregateCountMissing, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9008 << new QgsStaticExpressionFunction( u"minimum"_s, aggParams, fcnAggregateMin, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9009 << new QgsStaticExpressionFunction( u"maximum"_s, aggParams, fcnAggregateMax, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9010 << new QgsStaticExpressionFunction( u"sum"_s, aggParams, fcnAggregateSum, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9011 << new QgsStaticExpressionFunction( u"mean"_s, aggParams, fcnAggregateMean, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9012 << new QgsStaticExpressionFunction( u"median"_s, aggParams, fcnAggregateMedian, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9013 << new QgsStaticExpressionFunction( u"stdev"_s, aggParams, fcnAggregateStdev, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9014 << new QgsStaticExpressionFunction( u"range"_s, aggParams, fcnAggregateRange, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9015 << new QgsStaticExpressionFunction( u"minority"_s, aggParams, fcnAggregateMinority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9016 << new QgsStaticExpressionFunction( u"majority"_s, aggParams, fcnAggregateMajority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9017 << new QgsStaticExpressionFunction( u"q1"_s, aggParams, fcnAggregateQ1, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9018 << new QgsStaticExpressionFunction( u"q3"_s, aggParams, fcnAggregateQ3, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9019 << new QgsStaticExpressionFunction( u"iqr"_s, aggParams, fcnAggregateIQR, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9020 << new QgsStaticExpressionFunction( u"min_length"_s, aggParams, fcnAggregateMinLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9021 << new QgsStaticExpressionFunction( u"max_length"_s, aggParams, fcnAggregateMaxLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9022 << new QgsStaticExpressionFunction( u"collect"_s, aggParams, fcnAggregateCollectGeometry, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9023 << new QgsStaticExpressionFunction( u"concatenate"_s, aggParamsConcat, fcnAggregateStringConcat, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9024 << new QgsStaticExpressionFunction( u"concatenate_unique"_s, aggParamsConcat, fcnAggregateStringConcatUnique, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9025 << new QgsStaticExpressionFunction( u"array_agg"_s, aggParamsArray, fcnAggregateArray, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9026
9027 << new QgsStaticExpressionFunction( u"regexp_match"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpMatch, QStringList() << u"Conditionals"_s << u"String"_s )
9028 << new QgsStaticExpressionFunction( u"regexp_matches"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ) << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ), fcnRegexpMatches, u"Arrays"_s )
9029
9030 << new QgsStaticExpressionFunction( u"now"_s, 0, fcnNow, u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$now"_s )
9031 << new QgsStaticExpressionFunction( u"age"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime1"_s )
9032 << QgsExpressionFunction::Parameter( u"datetime2"_s ),
9033 fcnAge, u"Date and Time"_s )
9034 << new QgsStaticExpressionFunction( u"year"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnYear, u"Date and Time"_s )
9035 << new QgsStaticExpressionFunction( u"month"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnMonth, u"Date and Time"_s )
9036 << new QgsStaticExpressionFunction( u"week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnWeek, u"Date and Time"_s )
9037 << new QgsStaticExpressionFunction( u"day"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDay, u"Date and Time"_s )
9038 << new QgsStaticExpressionFunction( u"hour"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnHour, u"Date and Time"_s )
9039 << new QgsStaticExpressionFunction( u"minute"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnMinute, u"Date and Time"_s )
9040 << new QgsStaticExpressionFunction( u"second"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnSeconds, u"Date and Time"_s )
9041 << new QgsStaticExpressionFunction( u"epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnEpoch, u"Date and Time"_s )
9042 << new QgsStaticExpressionFunction( u"datetime_from_epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"long"_s ), fcnDateTimeFromEpoch, u"Date and Time"_s )
9043 << new QgsStaticExpressionFunction( u"day_of_week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDayOfWeek, u"Date and Time"_s )
9044 << new QgsStaticExpressionFunction( u"make_date"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"year"_s )
9045 << QgsExpressionFunction::Parameter( u"month"_s )
9046 << QgsExpressionFunction::Parameter( u"day"_s ),
9047 fcnMakeDate, u"Date and Time"_s )
9048 << new QgsStaticExpressionFunction( u"make_time"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hour"_s )
9049 << QgsExpressionFunction::Parameter( u"minute"_s )
9050 << QgsExpressionFunction::Parameter( u"second"_s ),
9051 fcnMakeTime, u"Date and Time"_s )
9052 << new QgsStaticExpressionFunction( u"make_datetime"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"year"_s )
9053 << QgsExpressionFunction::Parameter( u"month"_s )
9054 << QgsExpressionFunction::Parameter( u"day"_s )
9055 << QgsExpressionFunction::Parameter( u"hour"_s )
9056 << QgsExpressionFunction::Parameter( u"minute"_s )
9057 << QgsExpressionFunction::Parameter( u"second"_s ),
9058 fcnMakeDateTime, u"Date and Time"_s )
9059 << new QgsStaticExpressionFunction( u"make_interval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"years"_s, true, 0 )
9060 << QgsExpressionFunction::Parameter( u"months"_s, true, 0 )
9061 << QgsExpressionFunction::Parameter( u"weeks"_s, true, 0 )
9062 << QgsExpressionFunction::Parameter( u"days"_s, true, 0 )
9063 << QgsExpressionFunction::Parameter( u"hours"_s, true, 0 )
9064 << QgsExpressionFunction::Parameter( u"minutes"_s, true, 0 )
9065 << QgsExpressionFunction::Parameter( u"seconds"_s, true, 0 ),
9066 fcnMakeInterval, u"Date and Time"_s )
9067 << new QgsStaticExpressionFunction( u"timezone_from_id"_s, { QgsExpressionFunction::Parameter( u"id"_s ) }, fcnTimeZoneFromId, u"Date and Time"_s )
9068 << new QgsStaticExpressionFunction( u"timezone_id"_s, { QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnTimeZoneToId, u"Date and Time"_s )
9069 << new QgsStaticExpressionFunction( u"get_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ) }, fcnGetTimeZone, u"Date and Time"_s )
9070 << new QgsStaticExpressionFunction( u"set_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnSetTimeZone, u"Date and Time"_s )
9071 << new QgsStaticExpressionFunction( u"convert_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnConvertTimeZone, u"Date and Time"_s )
9072 << new QgsStaticExpressionFunction( u"lower"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnLower, u"String"_s )
9073 << new QgsStaticExpressionFunction( u"substr_count"_s, QgsExpressionFunction::ParameterList()
9074 << QgsExpressionFunction::Parameter( u"string"_s )
9075 << QgsExpressionFunction::Parameter( u"substring"_s )
9076 << QgsExpressionFunction::Parameter( u"overlapping"_s, true, false ), // Optional parameter with default value of false
9077 fcnSubstrCount,
9078 u"String"_s )
9079 << new QgsStaticExpressionFunction( u"upper"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnUpper, u"String"_s )
9080 << new QgsStaticExpressionFunction( u"title"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTitle, u"String"_s )
9081 << new QgsStaticExpressionFunction( u"trim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTrim, u"String"_s )
9082 << new QgsStaticExpressionFunction( u"unaccent"_s, { QgsExpressionFunction::Parameter( u"string"_s ) }, fcnUnaccent, u"String"_s )
9083 << new QgsStaticExpressionFunction( u"ltrim"_s, QgsExpressionFunction::ParameterList()
9084 << QgsExpressionFunction::Parameter( u"string"_s )
9085 << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnLTrim, u"String"_s )
9086 << new QgsStaticExpressionFunction( u"rtrim"_s, QgsExpressionFunction::ParameterList()
9087 << QgsExpressionFunction::Parameter( u"string"_s )
9088 << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnRTrim, u"String"_s )
9089 << new QgsStaticExpressionFunction( u"levenshtein"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLevenshtein, u"Fuzzy Matching"_s )
9090 << new QgsStaticExpressionFunction( u"longest_common_substring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLCS, u"Fuzzy Matching"_s )
9091 << new QgsStaticExpressionFunction( u"hamming_distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnHamming, u"Fuzzy Matching"_s )
9092 << new QgsStaticExpressionFunction( u"soundex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnSoundex, u"Fuzzy Matching"_s )
9093 << new QgsStaticExpressionFunction( u"char"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"code"_s ), fcnChar, u"String"_s )
9094 << new QgsStaticExpressionFunction( u"ascii"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnAscii, u"String"_s )
9095 << new QgsStaticExpressionFunction( u"wordwrap"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s ) << QgsExpressionFunction::Parameter( u"length"_s ) << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "" ), fcnWordwrap, u"String"_s )
9096 << new QgsStaticExpressionFunction( u"length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s, true, "" ), fcnLength, QStringList() << u"String"_s << u"GeometryGroup"_s )
9097 << new QgsStaticExpressionFunction( u"length3D"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLength3D, u"GeometryGroup"_s )
9098 << new QgsStaticExpressionFunction( u"repeat"_s, { QgsExpressionFunction::Parameter( u"text"_s ), QgsExpressionFunction::Parameter( u"number"_s )}, fcnRepeat, u"String"_s )
9099 << new QgsStaticExpressionFunction( u"replace"_s, -1, fcnReplace, u"String"_s )
9100 << new QgsStaticExpressionFunction( u"regexp_replace"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s )
9101 << QgsExpressionFunction::Parameter( u"replacement"_s ), fcnRegexpReplace, u"String"_s )
9102 << new QgsStaticExpressionFunction( u"regexp_substr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpSubstr, u"String"_s )
9103 << new QgsStaticExpressionFunction( u"substr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"length"_s, true ), fcnSubstr, u"String"_s, QString(),
9104 false, QSet< QString >(), false, QStringList(), true )
9105 << new QgsStaticExpressionFunction( u"concat"_s, -1, fcnConcat, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9106 << new QgsStaticExpressionFunction( u"strpos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"haystack"_s ) << QgsExpressionFunction::Parameter( u"needle"_s ), fcnStrpos, u"String"_s )
9107 << new QgsStaticExpressionFunction( u"left"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnLeft, u"String"_s )
9108 << new QgsStaticExpressionFunction( u"right"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnRight, u"String"_s )
9109 << new QgsStaticExpressionFunction( u"rpad"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s ), fcnRPad, u"String"_s )
9110 << new QgsStaticExpressionFunction( u"lpad"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s ), fcnLPad, u"String"_s )
9111 << new QgsStaticExpressionFunction( u"format"_s, -1, fcnFormatString, u"String"_s )
9112 << new QgsStaticExpressionFunction( u"format_number"_s, QgsExpressionFunction::ParameterList()
9113 << QgsExpressionFunction::Parameter( u"number"_s )
9114 << QgsExpressionFunction::Parameter( u"places"_s, true, 0 )
9115 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() )
9116 << QgsExpressionFunction::Parameter( u"omit_group_separators"_s, true, false )
9117 << QgsExpressionFunction::Parameter( u"trim_trailing_zeroes"_s, true, false ), fcnFormatNumber, u"String"_s )
9118 << new QgsStaticExpressionFunction( u"format_date"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ) << QgsExpressionFunction::Parameter( u"format"_s ) << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ), fcnFormatDate, QStringList() << u"String"_s << u"Date and Time"_s )
9119 << new QgsStaticExpressionFunction( u"color_grayscale_average"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ), fcnColorGrayscaleAverage, u"Color"_s )
9120 << new QgsStaticExpressionFunction( u"color_mix_rgb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s )
9121 << QgsExpressionFunction::Parameter( u"color2"_s )
9122 << QgsExpressionFunction::Parameter( u"ratio"_s ),
9123 fcnColorMixRgb, u"Color"_s )
9124 << new QgsStaticExpressionFunction( u"color_mix"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s )
9125 << QgsExpressionFunction::Parameter( u"color2"_s )
9126 << QgsExpressionFunction::Parameter( u"ratio"_s ),
9127 fcnColorMix, u"Color"_s )
9128 << new QgsStaticExpressionFunction( u"color_rgb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"red"_s )
9129 << QgsExpressionFunction::Parameter( u"green"_s )
9130 << QgsExpressionFunction::Parameter( u"blue"_s ),
9131 fcnColorRgb, u"Color"_s )
9132 << new QgsStaticExpressionFunction( u"color_rgbf"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"red"_s )
9133 << QgsExpressionFunction::Parameter( u"green"_s )
9134 << QgsExpressionFunction::Parameter( u"blue"_s )
9135 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9136 fcnColorRgbF, u"Color"_s )
9137 << new QgsStaticExpressionFunction( u"color_rgba"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"red"_s )
9138 << QgsExpressionFunction::Parameter( u"green"_s )
9139 << QgsExpressionFunction::Parameter( u"blue"_s )
9140 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9141 fncColorRgba, u"Color"_s )
9142 << new QgsStaticExpressionFunction( u"ramp_color"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s )
9143 << QgsExpressionFunction::Parameter( u"value"_s ),
9144 fcnRampColor, u"Color"_s )
9145 << new QgsStaticExpressionFunction( u"ramp_color_object"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s )
9146 << QgsExpressionFunction::Parameter( u"value"_s ),
9147 fcnRampColorObject, u"Color"_s )
9148 << new QgsStaticExpressionFunction( u"create_ramp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s )
9149 << QgsExpressionFunction::Parameter( u"discrete"_s, true, false ),
9150 fcnCreateRamp, u"Color"_s )
9151 << new QgsStaticExpressionFunction( u"color_hsl"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9152 << QgsExpressionFunction::Parameter( u"saturation"_s )
9153 << QgsExpressionFunction::Parameter( u"lightness"_s ),
9154 fcnColorHsl, u"Color"_s )
9155 << new QgsStaticExpressionFunction( u"color_hsla"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9156 << QgsExpressionFunction::Parameter( u"saturation"_s )
9157 << QgsExpressionFunction::Parameter( u"lightness"_s )
9158 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9159 fncColorHsla, u"Color"_s )
9160 << new QgsStaticExpressionFunction( u"color_hslf"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9161 << QgsExpressionFunction::Parameter( u"saturation"_s )
9162 << QgsExpressionFunction::Parameter( u"lightness"_s )
9163 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9164 fcnColorHslF, u"Color"_s )
9165 << new QgsStaticExpressionFunction( u"color_hsv"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9166 << QgsExpressionFunction::Parameter( u"saturation"_s )
9167 << QgsExpressionFunction::Parameter( u"value"_s ),
9168 fcnColorHsv, u"Color"_s )
9169 << new QgsStaticExpressionFunction( u"color_hsva"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9170 << QgsExpressionFunction::Parameter( u"saturation"_s )
9171 << QgsExpressionFunction::Parameter( u"value"_s )
9172 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9173 fncColorHsva, u"Color"_s )
9174 << new QgsStaticExpressionFunction( u"color_hsvf"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9175 << QgsExpressionFunction::Parameter( u"saturation"_s )
9176 << QgsExpressionFunction::Parameter( u"value"_s )
9177 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9178 fcnColorHsvF, u"Color"_s )
9179 << new QgsStaticExpressionFunction( u"color_cmyk"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"cyan"_s )
9180 << QgsExpressionFunction::Parameter( u"magenta"_s )
9181 << QgsExpressionFunction::Parameter( u"yellow"_s )
9182 << QgsExpressionFunction::Parameter( u"black"_s ),
9183 fcnColorCmyk, u"Color"_s )
9184 << new QgsStaticExpressionFunction( u"color_cmyka"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"cyan"_s )
9185 << QgsExpressionFunction::Parameter( u"magenta"_s )
9186 << QgsExpressionFunction::Parameter( u"yellow"_s )
9187 << QgsExpressionFunction::Parameter( u"black"_s )
9188 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9189 fncColorCmyka, u"Color"_s )
9190 << new QgsStaticExpressionFunction( u"color_cmykf"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"cyan"_s )
9191 << QgsExpressionFunction::Parameter( u"magenta"_s )
9192 << QgsExpressionFunction::Parameter( u"yellow"_s )
9193 << QgsExpressionFunction::Parameter( u"black"_s )
9194 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9195 fcnColorCmykF, u"Color"_s )
9196 << new QgsStaticExpressionFunction( u"color_part"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s )
9197 << QgsExpressionFunction::Parameter( u"component"_s ),
9198 fncColorPart, u"Color"_s )
9199 << new QgsStaticExpressionFunction( u"darker"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s )
9200 << QgsExpressionFunction::Parameter( u"factor"_s ),
9201 fncDarker, u"Color"_s )
9202 << new QgsStaticExpressionFunction( u"lighter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s )
9203 << QgsExpressionFunction::Parameter( u"factor"_s ),
9204 fncLighter, u"Color"_s )
9205 << new QgsStaticExpressionFunction( u"set_color_part"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fncSetColorPart, u"Color"_s )
9206
9207 // file info
9208 << new QgsStaticExpressionFunction( u"base_file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9209 fcnBaseFileName, u"Files and Paths"_s )
9210 << new QgsStaticExpressionFunction( u"file_suffix"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9211 fcnFileSuffix, u"Files and Paths"_s )
9212 << new QgsStaticExpressionFunction( u"file_exists"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9213 fcnFileExists, u"Files and Paths"_s )
9214 << new QgsStaticExpressionFunction( u"file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9215 fcnFileName, u"Files and Paths"_s )
9216 << new QgsStaticExpressionFunction( u"is_file"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9217 fcnPathIsFile, u"Files and Paths"_s )
9218 << new QgsStaticExpressionFunction( u"is_directory"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9219 fcnPathIsDir, u"Files and Paths"_s )
9220 << new QgsStaticExpressionFunction( u"file_path"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9221 fcnFilePath, u"Files and Paths"_s )
9222 << new QgsStaticExpressionFunction( u"file_size"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9223 fcnFileSize, u"Files and Paths"_s )
9224
9225 << new QgsStaticExpressionFunction( u"exif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ) << QgsExpressionFunction::Parameter( u"tag"_s, true ),
9226 fcnExif, u"Files and Paths"_s )
9227 << new QgsStaticExpressionFunction( u"exif_geotag"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9228 fcnExifGeoTag, u"GeometryGroup"_s )
9229
9230 // hash
9231 << new QgsStaticExpressionFunction( u"hash"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"method"_s ),
9232 fcnGenericHash, u"Conversions"_s )
9233 << new QgsStaticExpressionFunction( u"md5"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ),
9234 fcnHashMd5, u"Conversions"_s )
9235 << new QgsStaticExpressionFunction( u"sha256"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ),
9236 fcnHashSha256, u"Conversions"_s )
9237
9238 //base64
9239 << new QgsStaticExpressionFunction( u"to_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9240 fcnToBase64, u"Conversions"_s )
9241 << new QgsStaticExpressionFunction( u"from_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ),
9242 fcnFromBase64, u"Conversions"_s )
9243
9244 // magnetic models
9245 << new QgsStaticExpressionFunction( u"magnetic_declination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"model_name"_s )
9246 << QgsExpressionFunction::Parameter( u"date"_s )
9247 << QgsExpressionFunction::Parameter( u"latitude"_s )
9248 << QgsExpressionFunction::Parameter( u"longitude"_s )
9249 << QgsExpressionFunction::Parameter( u"height"_s )
9250 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9251 fcnMagneticDeclination, u"MagneticModels"_s )
9252 << new QgsStaticExpressionFunction( u"magnetic_inclination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"model_name"_s )
9253 << QgsExpressionFunction::Parameter( u"date"_s )
9254 << QgsExpressionFunction::Parameter( u"latitude"_s )
9255 << QgsExpressionFunction::Parameter( u"longitude"_s )
9256 << QgsExpressionFunction::Parameter( u"height"_s )
9257 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9258 fcnMagneticInclination, u"MagneticModels"_s )
9259 << new QgsStaticExpressionFunction( u"magnetic_declination_rate_of_change"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"model_name"_s )
9260 << QgsExpressionFunction::Parameter( u"date"_s )
9261 << QgsExpressionFunction::Parameter( u"latitude"_s )
9262 << QgsExpressionFunction::Parameter( u"longitude"_s )
9263 << QgsExpressionFunction::Parameter( u"height"_s )
9264 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9265 fcnMagneticDeclinationRateOfChange, u"MagneticModels"_s )
9266 << new QgsStaticExpressionFunction( u"magnetic_inclination_rate_of_change"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"model_name"_s )
9267 << QgsExpressionFunction::Parameter( u"date"_s )
9268 << QgsExpressionFunction::Parameter( u"latitude"_s )
9269 << QgsExpressionFunction::Parameter( u"longitude"_s )
9270 << QgsExpressionFunction::Parameter( u"height"_s )
9271 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9272 fcnMagneticInclinationRateOfChange, u"MagneticModels"_s )
9273
9274 // deprecated stuff - hidden from users
9275 << new QgsStaticExpressionFunction( u"$scale"_s, QgsExpressionFunction::ParameterList(), fcnMapScale, u"deprecated"_s );
9276
9277 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( u"$geometry"_s, 0, fcnGeometry, u"GeometryGroup"_s, QString(), true );
9278 geomFunc->setIsStatic( false );
9279 functions << geomFunc;
9280
9281 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( u"$area"_s, 0, fcnGeomArea, u"GeometryGroup"_s, QString(), true );
9282 areaFunc->setIsStatic( false );
9283 functions << areaFunc;
9284
9285 functions << new QgsStaticExpressionFunction( u"area"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnArea, u"GeometryGroup"_s );
9286
9287 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( u"$length"_s, 0, fcnGeomLength, u"GeometryGroup"_s, QString(), true );
9288 lengthFunc->setIsStatic( false );
9289 functions << lengthFunc;
9290
9291 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( u"$perimeter"_s, 0, fcnGeomPerimeter, u"GeometryGroup"_s, QString(), true );
9292 perimeterFunc->setIsStatic( false );
9293 functions << perimeterFunc;
9294
9295 functions << new QgsStaticExpressionFunction( u"perimeter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPerimeter, u"GeometryGroup"_s );
9296
9297 functions << new QgsStaticExpressionFunction( u"roundness"_s,
9298 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9299 fcnRoundness, u"GeometryGroup"_s );
9300
9301 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( u"$x"_s, 0, fcnX, u"GeometryGroup"_s, QString(), true );
9302 xFunc->setIsStatic( false );
9303 functions << xFunc;
9304
9305 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( u"$y"_s, 0, fcnY, u"GeometryGroup"_s, QString(), true );
9306 yFunc->setIsStatic( false );
9307 functions << yFunc;
9308
9309 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( u"$z"_s, 0, fcnZ, u"GeometryGroup"_s, QString(), true );
9310 zFunc->setIsStatic( false );
9311 functions << zFunc;
9312
9313 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
9314 {
9315 { u"overlay_intersects"_s, fcnGeomOverlayIntersects },
9316 { u"overlay_contains"_s, fcnGeomOverlayContains },
9317 { u"overlay_crosses"_s, fcnGeomOverlayCrosses },
9318 { u"overlay_equals"_s, fcnGeomOverlayEquals },
9319 { u"overlay_touches"_s, fcnGeomOverlayTouches },
9320 { u"overlay_disjoint"_s, fcnGeomOverlayDisjoint },
9321 { u"overlay_within"_s, fcnGeomOverlayWithin },
9322 };
9323 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
9324 while ( i.hasNext() )
9325 {
9326 i.next();
9327 QgsStaticExpressionFunction *fcnGeomOverlayFunc = new QgsStaticExpressionFunction( i.key(), QgsExpressionFunction::ParameterList()
9328 << QgsExpressionFunction::Parameter( u"layer"_s )
9329 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9330 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9331 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( -1 ), true )
9332 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false )
9333 << QgsExpressionFunction::Parameter( u"min_overlap"_s, true, QVariant( -1 ), false )
9334 << QgsExpressionFunction::Parameter( u"min_inscribed_circle_radius"_s, true, QVariant( -1 ), false )
9335 << QgsExpressionFunction::Parameter( u"return_details"_s, true, false, false )
9336 << QgsExpressionFunction::Parameter( u"sort_by_intersection_size"_s, true, QString(), false ),
9337 i.value(), u"GeometryGroup"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
9338
9339 // The current feature is accessed for the geometry, so this should not be cached
9340 fcnGeomOverlayFunc->setIsStatic( false );
9341 functions << fcnGeomOverlayFunc;
9342 }
9343
9344 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( u"overlay_nearest"_s, QgsExpressionFunction::ParameterList()
9345 << QgsExpressionFunction::Parameter( u"layer"_s )
9346 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9347 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9348 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( 1 ), true )
9349 << QgsExpressionFunction::Parameter( u"max_distance"_s, true, 0 )
9350 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false ),
9351 fcnGeomOverlayNearest, u"GeometryGroup"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
9352 // The current feature is accessed for the geometry, so this should not be cached
9353 fcnGeomOverlayNearestFunc->setIsStatic( false );
9354 functions << fcnGeomOverlayNearestFunc;
9355
9356 functions
9357 << new QgsStaticExpressionFunction( u"is_valid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsValid, u"GeometryGroup"_s )
9358 << new QgsStaticExpressionFunction( u"x"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomX, u"GeometryGroup"_s )
9359 << new QgsStaticExpressionFunction( u"y"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomY, u"GeometryGroup"_s )
9360 << new QgsStaticExpressionFunction( u"z"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomZ, u"GeometryGroup"_s )
9361 << new QgsStaticExpressionFunction( u"m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomM, u"GeometryGroup"_s )
9362 << new QgsStaticExpressionFunction( u"point_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnPointN, u"GeometryGroup"_s )
9363 << new QgsStaticExpressionFunction( u"start_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStartPoint, u"GeometryGroup"_s )
9364 << new QgsStaticExpressionFunction( u"end_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnEndPoint, u"GeometryGroup"_s )
9365 << new QgsStaticExpressionFunction( u"nodes_to_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9366 << QgsExpressionFunction::Parameter( u"ignore_closing_nodes"_s, true, false ),
9367 fcnNodesToPoints, u"GeometryGroup"_s )
9368 << new QgsStaticExpressionFunction( u"segments_to_lines"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSegmentsToLines, u"GeometryGroup"_s )
9369 << new QgsStaticExpressionFunction( u"collect_geometries"_s, -1, fcnCollectGeometries, u"GeometryGroup"_s )
9370 << new QgsStaticExpressionFunction( u"make_point"_s, -1, fcnMakePoint, u"GeometryGroup"_s )
9371 << new QgsStaticExpressionFunction( u"make_point_m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"x"_s )
9372 << QgsExpressionFunction::Parameter( u"y"_s )
9373 << QgsExpressionFunction::Parameter( u"m"_s ),
9374 fcnMakePointM, u"GeometryGroup"_s )
9375 << new QgsStaticExpressionFunction( u"make_line"_s, -1, fcnMakeLine, u"GeometryGroup"_s )
9376 << new QgsStaticExpressionFunction( u"make_polygon"_s, -1, fcnMakePolygon, u"GeometryGroup"_s )
9377 << new QgsStaticExpressionFunction( u"make_triangle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s )
9378 << QgsExpressionFunction::Parameter( u"point2"_s )
9379 << QgsExpressionFunction::Parameter( u"point3"_s ),
9380 fcnMakeTriangle, u"GeometryGroup"_s )
9381 << new QgsStaticExpressionFunction( u"make_circle"_s, QgsExpressionFunction::ParameterList()
9382 << QgsExpressionFunction::Parameter( u"center"_s )
9383 << QgsExpressionFunction::Parameter( u"radius"_s )
9384 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9385 fcnMakeCircle, u"GeometryGroup"_s )
9386 << new QgsStaticExpressionFunction( u"make_ellipse"_s, QgsExpressionFunction::ParameterList()
9387 << QgsExpressionFunction::Parameter( u"center"_s )
9388 << QgsExpressionFunction::Parameter( u"semi_major_axis"_s )
9389 << QgsExpressionFunction::Parameter( u"semi_minor_axis"_s )
9390 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9391 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9392 fcnMakeEllipse, u"GeometryGroup"_s )
9393 << new QgsStaticExpressionFunction( u"make_regular_polygon"_s, QgsExpressionFunction::ParameterList()
9394 << QgsExpressionFunction::Parameter( u"center"_s )
9395 << QgsExpressionFunction::Parameter( u"radius"_s )
9396 << QgsExpressionFunction::Parameter( u"number_sides"_s )
9397 << QgsExpressionFunction::Parameter( u"circle"_s, true, 0 ),
9398 fcnMakeRegularPolygon, u"GeometryGroup"_s )
9399 << new QgsStaticExpressionFunction( u"make_square"_s, QgsExpressionFunction::ParameterList()
9400 << QgsExpressionFunction::Parameter( u"point1"_s )
9401 << QgsExpressionFunction::Parameter( u"point2"_s ),
9402 fcnMakeSquare, u"GeometryGroup"_s )
9403 << new QgsStaticExpressionFunction( u"make_rectangle_3points"_s, QgsExpressionFunction::ParameterList()
9404 << QgsExpressionFunction::Parameter( u"point1"_s )
9405 << QgsExpressionFunction::Parameter( u"point2"_s )
9406 << QgsExpressionFunction::Parameter( u"point3"_s )
9407 << QgsExpressionFunction::Parameter( u"option"_s, true, 0 ),
9408 fcnMakeRectangleFrom3Points, u"GeometryGroup"_s )
9409 << new QgsStaticExpressionFunction( u"make_valid"_s, QgsExpressionFunction::ParameterList
9410 {
9411 QgsExpressionFunction::Parameter( u"geometry"_s ),
9412#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
9413 QgsExpressionFunction::Parameter( u"method"_s, true, u"linework"_s ),
9414#else
9415 QgsExpressionFunction::Parameter( u"method"_s, true, u"structure"_s ),
9416#endif
9417 QgsExpressionFunction::Parameter( u"keep_collapsed"_s, true, false )
9418 }, fcnGeomMakeValid, u"GeometryGroup"_s );
9419
9420 functions << new QgsStaticExpressionFunction( u"x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnXat, u"GeometryGroup"_s );
9421 functions << new QgsStaticExpressionFunction( u"y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnYat, u"GeometryGroup"_s );
9422 functions << new QgsStaticExpressionFunction( u"z_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnZat, u"GeometryGroup"_s );
9423 functions << new QgsStaticExpressionFunction( u"m_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnMat, u"GeometryGroup"_s );
9424
9425 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( u"$x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnOldXat, u"GeometryGroup"_s, QString(), true, QSet<QString>(), false, QStringList() << u"xat"_s );
9426 xAtFunc->setIsStatic( false );
9427 functions << xAtFunc;
9428
9429
9430 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( u"$y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnOldYat, u"GeometryGroup"_s, QString(), true, QSet<QString>(), false, QStringList() << u"yat"_s );
9431 yAtFunc->setIsStatic( false );
9432 functions << yAtFunc;
9433
9434 functions
9435 << new QgsStaticExpressionFunction( u"geometry_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeometryType, u"GeometryGroup"_s )
9436 << new QgsStaticExpressionFunction( u"x_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnXMin, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"xmin"_s )
9437 << new QgsStaticExpressionFunction( u"x_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnXMax, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"xmax"_s )
9438 << new QgsStaticExpressionFunction( u"y_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnYMin, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"ymin"_s )
9439 << new QgsStaticExpressionFunction( u"y_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnYMax, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"ymax"_s )
9440 << new QgsStaticExpressionFunction( u"geom_from_wkt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s ), fcnGeomFromWKT, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"geomFromWKT"_s )
9441 << new QgsStaticExpressionFunction( u"geom_from_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary"_s ), fcnGeomFromWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
9442 << new QgsStaticExpressionFunction( u"geom_from_gml"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"gml"_s ), fcnGeomFromGML, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"geomFromGML"_s )
9443 << new QgsStaticExpressionFunction( u"flip_coordinates"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnFlipCoordinates, u"GeometryGroup"_s )
9444 << new QgsStaticExpressionFunction( u"relate"_s, -1, fcnRelate, u"GeometryGroup"_s )
9445 << new QgsStaticExpressionFunction( u"intersects_bbox"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnBbox, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"bbox"_s )
9446 << new QgsStaticExpressionFunction( u"disjoint"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9447 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9448 fcnDisjoint, u"GeometryGroup"_s )
9449 << new QgsStaticExpressionFunction( u"intersects"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9450 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9451 fcnIntersects, u"GeometryGroup"_s )
9452 << new QgsStaticExpressionFunction( u"touches"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9453 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9454 fcnTouches, u"GeometryGroup"_s )
9455 << new QgsStaticExpressionFunction( u"crosses"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9456 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9457 fcnCrosses, u"GeometryGroup"_s )
9458 << new QgsStaticExpressionFunction( u"contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9459 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9460 fcnContains, u"GeometryGroup"_s )
9461 << new QgsStaticExpressionFunction( u"overlaps"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9462 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9463 fcnOverlaps, u"GeometryGroup"_s )
9464 << new QgsStaticExpressionFunction( u"within"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9465 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9466 fcnWithin, u"GeometryGroup"_s )
9467 << new QgsStaticExpressionFunction( u"equals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9468 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9469 fcnEquals, u"GeometryGroup"_s )
9470 << new QgsStaticExpressionFunction( u"translate"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9471 << QgsExpressionFunction::Parameter( u"dx"_s )
9472 << QgsExpressionFunction::Parameter( u"dy"_s ),
9473 fcnTranslate, u"GeometryGroup"_s )
9474 << new QgsStaticExpressionFunction( u"rotate"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9475 << QgsExpressionFunction::Parameter( u"rotation"_s )
9476 << QgsExpressionFunction::Parameter( u"center"_s, true )
9477 << QgsExpressionFunction::Parameter( u"per_part"_s, true, false ),
9478 fcnRotate, u"GeometryGroup"_s )
9479 << new QgsStaticExpressionFunction( u"scale"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9480 << QgsExpressionFunction::Parameter( u"x_scale"_s )
9481 << QgsExpressionFunction::Parameter( u"y_scale"_s )
9482 << QgsExpressionFunction::Parameter( u"center"_s, true ),
9483 fcnScale, u"GeometryGroup"_s )
9484 << new QgsStaticExpressionFunction( u"affine_transform"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9485 << QgsExpressionFunction::Parameter( u"delta_x"_s )
9486 << QgsExpressionFunction::Parameter( u"delta_y"_s )
9487 << QgsExpressionFunction::Parameter( u"rotation_z"_s )
9488 << QgsExpressionFunction::Parameter( u"scale_x"_s )
9489 << QgsExpressionFunction::Parameter( u"scale_y"_s )
9490 << QgsExpressionFunction::Parameter( u"delta_z"_s, true, 0 )
9491 << QgsExpressionFunction::Parameter( u"delta_m"_s, true, 0 )
9492 << QgsExpressionFunction::Parameter( u"scale_z"_s, true, 1 )
9493 << QgsExpressionFunction::Parameter( u"scale_m"_s, true, 1 ),
9494 fcnAffineTransform, u"GeometryGroup"_s )
9495 << new QgsStaticExpressionFunction( u"buffer"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9496 << QgsExpressionFunction::Parameter( u"distance"_s )
9497 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8 )
9498 << QgsExpressionFunction::Parameter( u"cap"_s, true, u"round"_s )
9499 << QgsExpressionFunction::Parameter( u"join"_s, true, u"round"_s )
9500 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2 ),
9501 fcnBuffer, u"GeometryGroup"_s )
9502 << new QgsStaticExpressionFunction( u"force_rhr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9503 fcnForceRHR, u"GeometryGroup"_s )
9504 << new QgsStaticExpressionFunction( u"force_polygon_cw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9505 fcnForcePolygonCW, u"GeometryGroup"_s )
9506 << new QgsStaticExpressionFunction( u"force_polygon_ccw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9507 fcnForcePolygonCCW, u"GeometryGroup"_s )
9508 << new QgsStaticExpressionFunction( u"wedge_buffer"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"center"_s )
9509 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9510 << QgsExpressionFunction::Parameter( u"width"_s )
9511 << QgsExpressionFunction::Parameter( u"outer_radius"_s )
9512 << QgsExpressionFunction::Parameter( u"inner_radius"_s, true, 0.0 ), fcnWedgeBuffer, u"GeometryGroup"_s )
9513 << new QgsStaticExpressionFunction( u"tapered_buffer"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9514 << QgsExpressionFunction::Parameter( u"start_width"_s )
9515 << QgsExpressionFunction::Parameter( u"end_width"_s )
9516 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
9517 , fcnTaperedBuffer, u"GeometryGroup"_s )
9518 << new QgsStaticExpressionFunction( u"buffer_by_m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9519 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
9520 , fcnBufferByM, u"GeometryGroup"_s )
9521 << new QgsStaticExpressionFunction( u"offset_curve"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9522 << QgsExpressionFunction::Parameter( u"distance"_s )
9523 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
9524 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
9525 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
9526 fcnOffsetCurve, u"GeometryGroup"_s )
9527 << new QgsStaticExpressionFunction( u"single_sided_buffer"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9528 << QgsExpressionFunction::Parameter( u"distance"_s )
9529 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
9530 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
9531 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
9532 fcnSingleSidedBuffer, u"GeometryGroup"_s )
9533 << new QgsStaticExpressionFunction( u"extend"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9534 << QgsExpressionFunction::Parameter( u"start_distance"_s )
9535 << QgsExpressionFunction::Parameter( u"end_distance"_s ),
9536 fcnExtend, u"GeometryGroup"_s )
9537 << new QgsStaticExpressionFunction( u"centroid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCentroid, u"GeometryGroup"_s )
9538 << new QgsStaticExpressionFunction( u"point_on_surface"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPointOnSurface, u"GeometryGroup"_s )
9539 << new QgsStaticExpressionFunction( u"pole_of_inaccessibility"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9540 << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnPoleOfInaccessibility, u"GeometryGroup"_s )
9541 << new QgsStaticExpressionFunction( u"reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnReverse, { u"String"_s, u"GeometryGroup"_s } )
9542 << new QgsStaticExpressionFunction( u"exterior_ring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnExteriorRing, u"GeometryGroup"_s )
9543 << new QgsStaticExpressionFunction( u"interior_ring_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9544 << QgsExpressionFunction::Parameter( u"index"_s ),
9545 fcnInteriorRingN, u"GeometryGroup"_s )
9546 << new QgsStaticExpressionFunction( u"geometry_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9547 << QgsExpressionFunction::Parameter( u"index"_s ),
9548 fcnGeometryN, u"GeometryGroup"_s )
9549 << new QgsStaticExpressionFunction( u"boundary"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundary, u"GeometryGroup"_s )
9550 << new QgsStaticExpressionFunction( u"line_merge"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLineMerge, u"GeometryGroup"_s )
9551 << new QgsStaticExpressionFunction( u"shared_paths"_s, QgsExpressionFunction::ParameterList
9552 {
9553 QgsExpressionFunction::Parameter( u"geometry1"_s ),
9554 QgsExpressionFunction::Parameter( u"geometry2"_s )
9555 }, fcnSharedPaths, u"GeometryGroup"_s )
9556 << new QgsStaticExpressionFunction( u"bounds"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBounds, u"GeometryGroup"_s )
9557 << new QgsStaticExpressionFunction( u"simplify"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplify, u"GeometryGroup"_s )
9558 << new QgsStaticExpressionFunction( u"simplify_vw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplifyVW, u"GeometryGroup"_s )
9559 << new QgsStaticExpressionFunction( u"smooth"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"iterations"_s, true, 1 )
9560 << QgsExpressionFunction::Parameter( u"offset"_s, true, 0.25 )
9561 << QgsExpressionFunction::Parameter( u"min_length"_s, true, -1 )
9562 << QgsExpressionFunction::Parameter( u"max_angle"_s, true, 180 ), fcnSmooth, u"GeometryGroup"_s )
9563 << new QgsStaticExpressionFunction( u"triangular_wave"_s,
9564 {
9565 QgsExpressionFunction::Parameter( u"geometry"_s ),
9566 QgsExpressionFunction::Parameter( u"wavelength"_s ),
9567 QgsExpressionFunction::Parameter( u"amplitude"_s ),
9568 QgsExpressionFunction::Parameter( u"strict"_s, true, false )
9569 }, fcnTriangularWave, u"GeometryGroup"_s )
9570 << new QgsStaticExpressionFunction( u"triangular_wave_randomized"_s,
9571 {
9572 QgsExpressionFunction::Parameter( u"geometry"_s ),
9573 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
9574 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
9575 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
9576 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
9577 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 )
9578 }, fcnTriangularWaveRandomized, u"GeometryGroup"_s )
9579 << new QgsStaticExpressionFunction( u"square_wave"_s,
9580 {
9581 QgsExpressionFunction::Parameter( u"geometry"_s ),
9582 QgsExpressionFunction::Parameter( u"wavelength"_s ),
9583 QgsExpressionFunction::Parameter( u"amplitude"_s ),
9584 QgsExpressionFunction::Parameter( u"strict"_s, true, false )
9585 }, fcnSquareWave, u"GeometryGroup"_s )
9586 << new QgsStaticExpressionFunction( u"square_wave_randomized"_s,
9587 {
9588 QgsExpressionFunction::Parameter( u"geometry"_s ),
9589 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
9590 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
9591 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
9592 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
9593 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 )
9594 }, fcnSquareWaveRandomized, u"GeometryGroup"_s )
9595 << new QgsStaticExpressionFunction( u"wave"_s,
9596 {
9597 QgsExpressionFunction::Parameter( u"geometry"_s ),
9598 QgsExpressionFunction::Parameter( u"wavelength"_s ),
9599 QgsExpressionFunction::Parameter( u"amplitude"_s ),
9600 QgsExpressionFunction::Parameter( u"strict"_s, true, false )
9601 }, fcnRoundWave, u"GeometryGroup"_s )
9602 << new QgsStaticExpressionFunction( u"wave_randomized"_s,
9603 {
9604 QgsExpressionFunction::Parameter( u"geometry"_s ),
9605 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
9606 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
9607 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
9608 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
9609 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 )
9610 }, fcnRoundWaveRandomized, u"GeometryGroup"_s )
9611 << new QgsStaticExpressionFunction( u"apply_dash_pattern"_s,
9612 {
9613 QgsExpressionFunction::Parameter( u"geometry"_s ),
9614 QgsExpressionFunction::Parameter( u"pattern"_s ),
9615 QgsExpressionFunction::Parameter( u"start_rule"_s, true, u"no_rule"_s ),
9616 QgsExpressionFunction::Parameter( u"end_rule"_s, true, u"no_rule"_s ),
9617 QgsExpressionFunction::Parameter( u"adjustment"_s, true, u"both"_s ),
9618 QgsExpressionFunction::Parameter( u"pattern_offset"_s, true, 0 ),
9619 }, fcnApplyDashPattern, u"GeometryGroup"_s )
9620 << new QgsStaticExpressionFunction( u"densify_by_count"_s,
9621 {
9622 QgsExpressionFunction::Parameter( u"geometry"_s ),
9623 QgsExpressionFunction::Parameter( u"vertices"_s )
9624 }, fcnDensifyByCount, u"GeometryGroup"_s )
9625 << new QgsStaticExpressionFunction( u"densify_by_distance"_s,
9626 {
9627 QgsExpressionFunction::Parameter( u"geometry"_s ),
9628 QgsExpressionFunction::Parameter( u"distance"_s )
9629 }, fcnDensifyByDistance, u"GeometryGroup"_s )
9630 << new QgsStaticExpressionFunction( u"num_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumPoints, u"GeometryGroup"_s )
9631 << new QgsStaticExpressionFunction( u"num_interior_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumInteriorRings, u"GeometryGroup"_s )
9632 << new QgsStaticExpressionFunction( u"num_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumRings, u"GeometryGroup"_s )
9633 << new QgsStaticExpressionFunction( u"num_geometries"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumGeometries, u"GeometryGroup"_s )
9634 << new QgsStaticExpressionFunction( u"bounds_width"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsWidth, u"GeometryGroup"_s )
9635 << new QgsStaticExpressionFunction( u"bounds_height"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsHeight, u"GeometryGroup"_s )
9636 << new QgsStaticExpressionFunction( u"is_closed"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsClosed, u"GeometryGroup"_s )
9637 << new QgsStaticExpressionFunction( u"close_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCloseLine, u"GeometryGroup"_s )
9638 << new QgsStaticExpressionFunction( u"is_empty"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsEmpty, u"GeometryGroup"_s )
9639 << new QgsStaticExpressionFunction( u"is_empty_or_null"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsEmptyOrNull, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9640 << new QgsStaticExpressionFunction( u"convex_hull"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnConvexHull, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"convexHull"_s )
9641#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
9642 << new QgsStaticExpressionFunction( u"concave_hull"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9643 << QgsExpressionFunction::Parameter( u"target_percent"_s )
9644 << QgsExpressionFunction::Parameter( u"allow_holes"_s, true, false ), fcnConcaveHull, u"GeometryGroup"_s )
9645#endif
9646 << new QgsStaticExpressionFunction( u"oriented_bbox"_s, QgsExpressionFunction::ParameterList()
9647 << QgsExpressionFunction::Parameter( u"geometry"_s ),
9648 fcnOrientedBBox, u"GeometryGroup"_s )
9649 << new QgsStaticExpressionFunction( u"main_angle"_s, QgsExpressionFunction::ParameterList()
9650 << QgsExpressionFunction::Parameter( u"geometry"_s ),
9651 fcnMainAngle, u"GeometryGroup"_s )
9652 << new QgsStaticExpressionFunction( u"minimal_circle"_s, QgsExpressionFunction::ParameterList()
9653 << QgsExpressionFunction::Parameter( u"geometry"_s )
9654 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9655 fcnMinimalCircle, u"GeometryGroup"_s )
9656 << new QgsStaticExpressionFunction( u"difference"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9657 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9658 fcnDifference, u"GeometryGroup"_s )
9659 << new QgsStaticExpressionFunction( u"distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9660 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9661 fcnDistance, u"GeometryGroup"_s )
9662 << new QgsStaticExpressionFunction( u"hausdorff_distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s )
9663 << QgsExpressionFunction::Parameter( u"densify_fraction"_s, true ),
9664 fcnHausdorffDistance, u"GeometryGroup"_s )
9665 << new QgsStaticExpressionFunction( u"intersection"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9666 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9667 fcnIntersection, u"GeometryGroup"_s )
9668 << new QgsStaticExpressionFunction( u"sym_difference"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9669 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9670 fcnSymDifference, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"symDifference"_s )
9671 << new QgsStaticExpressionFunction( u"combine"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9672 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9673 fcnCombine, u"GeometryGroup"_s )
9674 << new QgsStaticExpressionFunction( u"union"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9675 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9676 fcnCombine, u"GeometryGroup"_s )
9677 << new QgsStaticExpressionFunction( u"geom_to_wkt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9678 << QgsExpressionFunction::Parameter( u"precision"_s, true, 8.0 ),
9679 fcnGeomToWKT, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"geomToWKT"_s )
9680 << new QgsStaticExpressionFunction( u"geom_to_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9681 fcnGeomToWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
9682 << new QgsStaticExpressionFunction( u"geometry"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetGeometry, u"GeometryGroup"_s, QString(), true )
9683 << new QgsStaticExpressionFunction( u"transform"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9684 << QgsExpressionFunction::Parameter( u"source_auth_id"_s )
9685 << QgsExpressionFunction::Parameter( u"dest_auth_id"_s ),
9686 fcnTransformGeometry, u"GeometryGroup"_s )
9687 << new QgsStaticExpressionFunction( u"extrude"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9688 << QgsExpressionFunction::Parameter( u"x"_s )
9689 << QgsExpressionFunction::Parameter( u"y"_s ),
9690 fcnExtrude, u"GeometryGroup"_s, QString() )
9691 << new QgsStaticExpressionFunction( u"is_multipart"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9692 fcnGeomIsMultipart, u"GeometryGroup"_s )
9693 << new QgsStaticExpressionFunction( u"z_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9694 fcnZMax, u"GeometryGroup"_s )
9695 << new QgsStaticExpressionFunction( u"z_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9696 fcnZMin, u"GeometryGroup"_s )
9697 << new QgsStaticExpressionFunction( u"m_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9698 fcnMMax, u"GeometryGroup"_s )
9699 << new QgsStaticExpressionFunction( u"m_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9700 fcnMMin, u"GeometryGroup"_s )
9701 << new QgsStaticExpressionFunction( u"sinuosity"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9702 fcnSinuosity, u"GeometryGroup"_s )
9703 << new QgsStaticExpressionFunction( u"straight_distance_2d"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9704 fcnStraightDistance2d, u"GeometryGroup"_s );
9705
9706
9707 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( u"order_parts"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9708 << QgsExpressionFunction::Parameter( u"orderby"_s )
9709 << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ),
9710 fcnOrderParts, u"GeometryGroup"_s, QString() );
9711
9712 orderPartsFunc->setIsStaticFunction(
9713 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9714 {
9715 const QList< QgsExpressionNode *> argList = node->args()->list();
9716 for ( QgsExpressionNode *argNode : argList )
9717 {
9718 if ( !argNode->isStatic( parent, context ) )
9719 return false;
9720 }
9721
9722 if ( node->args()->count() > 1 )
9723 {
9724 QgsExpressionNode *argNode = node->args()->at( 1 );
9725
9726 QString expString = argNode->eval( parent, context ).toString();
9727
9728 QgsExpression e( expString );
9729
9730 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9731 return true;
9732 }
9733
9734 return true;
9735 } );
9736
9737 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9738 {
9739 if ( node->args()->count() > 1 )
9740 {
9741 QgsExpressionNode *argNode = node->args()->at( 1 );
9742 QString expression = argNode->eval( parent, context ).toString();
9744 e.prepare( context );
9745 context->setCachedValue( expression, QVariant::fromValue( e ) );
9746 }
9747 return true;
9748 }
9749 );
9750 functions << orderPartsFunc;
9751
9752 functions
9753 << new QgsStaticExpressionFunction( u"closest_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9754 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9755 fcnClosestPoint, u"GeometryGroup"_s )
9756 << new QgsStaticExpressionFunction( u"shortest_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9757 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9758 fcnShortestLine, u"GeometryGroup"_s )
9759 << new QgsStaticExpressionFunction( u"line_interpolate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9760 << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolatePoint, u"GeometryGroup"_s )
9761 << new QgsStaticExpressionFunction( u"line_interpolate_point_by_m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9762 << QgsExpressionFunction::Parameter( u"m"_s ) << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
9763 fcnLineInterpolatePointByM, u"GeometryGroup"_s )
9764 << new QgsStaticExpressionFunction( u"line_interpolate_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9765 << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolateAngle, u"GeometryGroup"_s )
9766 << new QgsStaticExpressionFunction( u"line_locate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9767 << QgsExpressionFunction::Parameter( u"point"_s ), fcnLineLocatePoint, u"GeometryGroup"_s )
9768 << new QgsStaticExpressionFunction( u"line_locate_m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9769 << QgsExpressionFunction::Parameter( u"m"_s ) << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
9770 fcnLineLocateM, u"GeometryGroup"_s )
9771 << new QgsStaticExpressionFunction( u"angle_at_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9772 << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnAngleAtVertex, u"GeometryGroup"_s )
9773 << new QgsStaticExpressionFunction( u"distance_to_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9774 << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnDistanceToVertex, u"GeometryGroup"_s )
9775 << new QgsStaticExpressionFunction( u"line_substring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9776 << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ), fcnLineSubset, u"GeometryGroup"_s );
9777
9778
9779 // **Record** functions
9780
9781 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( u"$id"_s, 0, fcnFeatureId, u"Record and Attributes"_s );
9782 idFunc->setIsStatic( false );
9783 functions << idFunc;
9784
9785 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( u"$currentfeature"_s, 0, fcnFeature, u"Record and Attributes"_s );
9786 currentFeatureFunc->setIsStatic( false );
9787 functions << currentFeatureFunc;
9788
9789 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction( u"uuid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"format"_s, true, u"WithBraces"_s ), fcnUuid, u"Record and Attributes"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$uuid"_s );
9790 uuidFunc->setIsStatic( false );
9791 functions << uuidFunc;
9792
9793 functions
9794 << new QgsStaticExpressionFunction( u"feature_id"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetFeatureId, u"Record and Attributes"_s, QString(), true )
9795 << new QgsStaticExpressionFunction( u"get_feature"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s )
9796 << QgsExpressionFunction::Parameter( u"attribute"_s )
9797 << QgsExpressionFunction::Parameter( u"value"_s, true ),
9798 fcnGetFeature, u"Record and Attributes"_s, QString(), false, QSet<QString>(), false, QStringList() << u"QgsExpressionUtils::getFeature"_s )
9799 << new QgsStaticExpressionFunction( u"get_feature_by_id"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s )
9800 << QgsExpressionFunction::Parameter( u"feature_id"_s ),
9801 fcnGetFeatureById, u"Record and Attributes"_s, QString(), false, QSet<QString>(), false );
9802
9803 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( u"attributes"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s, true ),
9804 fcnAttributes, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9805 attributesFunc->setIsStatic( false );
9806 functions << attributesFunc;
9807 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( u"represent_attributes"_s, -1,
9808 fcnRepresentAttributes, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9809 representAttributesFunc->setIsStatic( false );
9810 functions << representAttributesFunc;
9811
9812 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction( u"is_feature_valid"_s,
9813 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s, true )
9814 << QgsExpressionFunction::Parameter( u"feature"_s, true )
9815 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
9816 fcnValidateFeature, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9817 validateFeature->setIsStatic( false );
9818 functions << validateFeature;
9819
9820 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction( u"is_attribute_valid"_s,
9821 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"attribute"_s, false )
9822 << QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s, true )
9823 << QgsExpressionFunction::Parameter( u"feature"_s, true )
9824 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
9825 fcnValidateAttribute, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9826 validateAttribute->setIsStatic( false );
9827 functions << validateAttribute;
9828
9829 QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction(
9830 u"maptip"_s,
9831 -1,
9832 fcnFeatureMaptip,
9833 u"Record and Attributes"_s,
9834 QString(),
9835 false,
9836 QSet<QString>()
9837 );
9838 maptipFunc->setIsStatic( false );
9839 functions << maptipFunc;
9840
9841 QgsStaticExpressionFunction *displayFunc = new QgsStaticExpressionFunction(
9842 u"display_expression"_s,
9843 -1,
9844 fcnFeatureDisplayExpression,
9845 u"Record and Attributes"_s,
9846 QString(),
9847 false,
9848 QSet<QString>()
9849 );
9850 displayFunc->setIsStatic( false );
9851 functions << displayFunc;
9852
9853 QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction(
9854 u"is_selected"_s,
9855 -1,
9856 fcnIsSelected,
9857 u"Record and Attributes"_s,
9858 QString(),
9859 false,
9860 QSet<QString>()
9861 );
9862 isSelectedFunc->setIsStatic( false );
9863 functions << isSelectedFunc;
9864
9865 functions
9866 << new QgsStaticExpressionFunction(
9867 u"num_selected"_s,
9868 -1,
9869 fcnNumSelected,
9870 u"Record and Attributes"_s,
9871 QString(),
9872 false,
9873 QSet<QString>()
9874 );
9875
9876 functions
9877 << new QgsStaticExpressionFunction(
9878 u"sqlite_fetch_and_increment"_s,
9880 << QgsExpressionFunction::Parameter( u"database"_s )
9881 << QgsExpressionFunction::Parameter( u"table"_s )
9882 << QgsExpressionFunction::Parameter( u"id_field"_s )
9883 << QgsExpressionFunction::Parameter( u"filter_attribute"_s )
9884 << QgsExpressionFunction::Parameter( u"filter_value"_s )
9885 << QgsExpressionFunction::Parameter( u"default_values"_s, true ),
9886 fcnSqliteFetchAndIncrement,
9887 u"Record and Attributes"_s
9888 );
9889
9890 // **CRS** functions
9891 functions
9892 << new QgsStaticExpressionFunction( u"crs_to_authid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"crs"_s ), fcnCrsToAuthid, u"CRS"_s, QString(), true )
9893 << new QgsStaticExpressionFunction( u"crs_from_text"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"definition"_s ), fcnCrsFromText, u"CRS"_s );
9894
9895
9896 // **Fields and Values** functions
9897 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( u"represent_value"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"attribute"_s ) << QgsExpressionFunction::Parameter( u"field_name"_s, true ), fcnRepresentValue, u"Record and Attributes"_s );
9898
9899 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9900 {
9901 Q_UNUSED( context )
9902 if ( node->args()->count() == 1 )
9903 {
9904 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
9905 if ( colRef )
9906 {
9907 return true;
9908 }
9909 else
9910 {
9911 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
9912 return false;
9913 }
9914 }
9915 else if ( node->args()->count() == 2 )
9916 {
9917 return true;
9918 }
9919 else
9920 {
9921 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
9922 return false;
9923 }
9924 }
9925 );
9926
9927 functions << representValueFunc;
9928
9929 // **General** functions
9930 functions
9931 << new QgsStaticExpressionFunction( u"layer_property"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s )
9932 << QgsExpressionFunction::Parameter( u"property"_s ),
9933 fcnGetLayerProperty, u"Map Layers"_s )
9934 << new QgsStaticExpressionFunction( u"decode_uri"_s,
9936 << QgsExpressionFunction::Parameter( u"layer"_s )
9937 << QgsExpressionFunction::Parameter( u"part"_s, true ),
9938 fcnDecodeUri, u"Map Layers"_s )
9939 << new QgsStaticExpressionFunction( u"mime_type"_s,
9941 << QgsExpressionFunction::Parameter( u"binary_data"_s ),
9942 fcnMimeType, u"General"_s )
9943 << new QgsStaticExpressionFunction( u"raster_statistic"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s )
9944 << QgsExpressionFunction::Parameter( u"band"_s )
9945 << QgsExpressionFunction::Parameter( u"statistic"_s ), fcnGetRasterBandStat, u"Rasters"_s );
9946
9947 // **var** function
9948 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( u"var"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnGetVariable, u"General"_s );
9949 varFunction->setIsStaticFunction(
9950 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9951 {
9952 /* A variable node is static if it has a static name and the name can be found at prepare
9953 * time and is tagged with isStatic.
9954 * It is not static if a variable is set during iteration or not tagged isStatic.
9955 * (e.g. geom_part variable)
9956 */
9957 if ( node->args()->count() > 0 )
9958 {
9959 QgsExpressionNode *argNode = node->args()->at( 0 );
9960
9961 if ( !argNode->isStatic( parent, context ) )
9962 return false;
9963
9964 const QString varName = argNode->eval( parent, context ).toString();
9965 if ( varName == "feature"_L1 || varName == "id"_L1 || varName == "geometry"_L1 )
9966 return false;
9967
9968 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
9969 return scope ? scope->isStatic( varName ) : false;
9970 }
9971 return false;
9972 }
9973 );
9974 varFunction->setUsesGeometryFunction(
9975 []( const QgsExpressionNodeFunction * node ) -> bool
9976 {
9977 if ( node && node->args()->count() > 0 )
9978 {
9979 QgsExpressionNode *argNode = node->args()->at( 0 );
9980 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
9981 {
9982 if ( literal->value() == "geometry"_L1 || literal->value() == "feature"_L1 )
9983 return true;
9984 }
9985 }
9986 return false;
9987 }
9988 );
9989
9990 functions
9991 << varFunction;
9992
9993 QgsStaticExpressionFunction *evalTemplateFunction = new QgsStaticExpressionFunction( u"eval_template"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"template"_s ), fcnEvalTemplate, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9994 evalTemplateFunction->setIsStaticFunction(
9995 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9996 {
9997 if ( node->args()->count() > 0 )
9998 {
9999 QgsExpressionNode *argNode = node->args()->at( 0 );
10000
10001 if ( argNode->isStatic( parent, context ) )
10002 {
10003 QString expString = argNode->eval( parent, context ).toString();
10004
10005 QgsExpression e( expString );
10006
10007 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10008 return true;
10009 }
10010 }
10011
10012 return false;
10013 } );
10014 functions << evalTemplateFunction;
10015
10016 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( u"eval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ), fcnEval, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10017 evalFunc->setIsStaticFunction(
10018 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
10019 {
10020 if ( node->args()->count() > 0 )
10021 {
10022 QgsExpressionNode *argNode = node->args()->at( 0 );
10023
10024 if ( argNode->isStatic( parent, context ) )
10025 {
10026 QString expString = argNode->eval( parent, context ).toString();
10027
10028 QgsExpression e( expString );
10029
10030 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10031 return true;
10032 }
10033 }
10034
10035 return false;
10036 } );
10037
10038 functions << evalFunc;
10039
10040 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( u"attribute"_s, -1, fcnAttribute, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10041 attributeFunc->setIsStaticFunction(
10042 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
10043 {
10044 const QList< QgsExpressionNode *> argList = node->args()->list();
10045 for ( QgsExpressionNode *argNode : argList )
10046 {
10047 if ( !argNode->isStatic( parent, context ) )
10048 return false;
10049 }
10050
10051 if ( node->args()->count() == 1 )
10052 {
10053 // not static -- this is the variant which uses the current feature taken direct from the expression context
10054 return false;
10055 }
10056
10057 return true;
10058 } );
10059 functions << attributeFunc;
10060
10061 functions
10062 << new QgsStaticExpressionFunction( u"env"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnEnvVar, u"General"_s, QString() )
10063 << new QgsWithVariableExpressionFunction()
10064 << new QgsStaticExpressionFunction( u"raster_value"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ), fcnRasterValue, u"Rasters"_s )
10065 << new QgsStaticExpressionFunction( u"raster_attributes"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ), fcnRasterAttributes, u"Rasters"_s )
10066
10067 // functions for arrays
10068 << new QgsArrayForeachExpressionFunction()
10069 << new QgsArrayFilterExpressionFunction()
10070 << new QgsStaticExpressionFunction( u"array"_s, -1, fcnArray, u"Arrays"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
10071 << new QgsStaticExpressionFunction( u"array_sort"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ), fcnArraySort, u"Arrays"_s )
10072 << new QgsStaticExpressionFunction( u"array_length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLength, u"Arrays"_s )
10073 << new QgsStaticExpressionFunction( u"array_contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayContains, u"Arrays"_s )
10074 << new QgsStaticExpressionFunction( u"array_count"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayCount, u"Arrays"_s )
10075 << new QgsStaticExpressionFunction( u"array_all"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array_a"_s ) << QgsExpressionFunction::Parameter( u"array_b"_s ), fcnArrayAll, u"Arrays"_s )
10076 << new QgsStaticExpressionFunction( u"array_find"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayFind, u"Arrays"_s )
10077 << new QgsStaticExpressionFunction( u"array_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayGet, u"Arrays"_s )
10078 << new QgsStaticExpressionFunction( u"array_first"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayFirst, u"Arrays"_s )
10079 << new QgsStaticExpressionFunction( u"array_last"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLast, u"Arrays"_s )
10080 << new QgsStaticExpressionFunction( u"array_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMinimum, u"Arrays"_s )
10081 << new QgsStaticExpressionFunction( u"array_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMaximum, u"Arrays"_s )
10082 << new QgsStaticExpressionFunction( u"array_mean"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMean, u"Arrays"_s )
10083 << new QgsStaticExpressionFunction( u"array_median"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMedian, u"Arrays"_s )
10084 << new QgsStaticExpressionFunction( u"array_majority"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"option"_s, true, QVariant( "all" ) ), fcnArrayMajority, u"Arrays"_s )
10085 << new QgsStaticExpressionFunction( u"array_minority"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"option"_s, true, QVariant( "all" ) ), fcnArrayMinority, u"Arrays"_s )
10086 << new QgsStaticExpressionFunction( u"array_sum"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArraySum, u"Arrays"_s )
10087 << new QgsStaticExpressionFunction( u"array_append"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayAppend, u"Arrays"_s )
10088 << new QgsStaticExpressionFunction( u"array_prepend"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayPrepend, u"Arrays"_s )
10089 << new QgsStaticExpressionFunction( u"array_insert"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayInsert, u"Arrays"_s )
10090 << new QgsStaticExpressionFunction( u"array_remove_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayRemoveAt, u"Arrays"_s )
10091 << new QgsStaticExpressionFunction( u"array_remove_all"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayRemoveAll, u"Arrays"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
10092 << new QgsStaticExpressionFunction( u"array_replace"_s, -1, fcnArrayReplace, u"Arrays"_s )
10093 << new QgsStaticExpressionFunction( u"array_prioritize"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"array_prioritize"_s ), fcnArrayPrioritize, u"Arrays"_s )
10094 << new QgsStaticExpressionFunction( u"array_cat"_s, -1, fcnArrayCat, u"Arrays"_s )
10095 << new QgsStaticExpressionFunction( u"array_slice"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"start_pos"_s ) << QgsExpressionFunction::Parameter( u"end_pos"_s ), fcnArraySlice, u"Arrays"_s )
10096 << new QgsStaticExpressionFunction( u"array_reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayReverse, u"Arrays"_s )
10097 << new QgsStaticExpressionFunction( u"array_intersect"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayIntersect, u"Arrays"_s )
10098 << new QgsStaticExpressionFunction( u"array_distinct"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayDistinct, u"Arrays"_s )
10099 << new QgsStaticExpressionFunction( u"array_to_string"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," ) << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ), fcnArrayToString, u"Arrays"_s )
10100 << new QgsStaticExpressionFunction( u"string_to_array"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," ) << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ), fcnStringToArray, u"Arrays"_s )
10101 << new QgsStaticExpressionFunction( u"generate_series"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"stop"_s ) << QgsExpressionFunction::Parameter( u"step"_s, true, 1.0 ), fcnGenerateSeries, u"Arrays"_s )
10102 << new QgsStaticExpressionFunction( u"geometries_to_array"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometries"_s ), fcnGeometryCollectionAsArray, u"Arrays"_s )
10103
10104 //functions for maps
10105 << new QgsStaticExpressionFunction( u"from_json"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLoadJson, u"Maps"_s, QString(), false, QSet<QString>(), false, QStringList() << u"json_to_map"_s )
10106 << new QgsStaticExpressionFunction( u"to_json"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"json_string"_s ), fcnWriteJson, u"Maps"_s, QString(), false, QSet<QString>(), false, QStringList() << u"map_to_json"_s )
10107 << new QgsStaticExpressionFunction( u"hstore_to_map"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHstoreToMap, u"Maps"_s )
10108 << new QgsStaticExpressionFunction( u"map_to_hstore"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHstore, u"Maps"_s )
10109 << new QgsStaticExpressionFunction( u"map"_s, -1, fcnMap, u"Maps"_s )
10110 << new QgsStaticExpressionFunction( u"map_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapGet, u"Maps"_s )
10111 << new QgsStaticExpressionFunction( u"map_exist"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapExist, u"Maps"_s )
10112 << new QgsStaticExpressionFunction( u"map_delete"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapDelete, u"Maps"_s )
10113 << new QgsStaticExpressionFunction( u"map_insert"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnMapInsert, u"Maps"_s )
10114 << new QgsStaticExpressionFunction( u"map_concat"_s, -1, fcnMapConcat, u"Maps"_s )
10115 << new QgsStaticExpressionFunction( u"map_akeys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAKeys, u"Maps"_s )
10116 << new QgsStaticExpressionFunction( u"map_avals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAVals, u"Maps"_s )
10117 << new QgsStaticExpressionFunction( u"map_prefix_keys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s )
10118 << QgsExpressionFunction::Parameter( u"prefix"_s ),
10119 fcnMapPrefixKeys, u"Maps"_s )
10120 << new QgsStaticExpressionFunction( u"map_to_html_table"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ),
10121 fcnMapToHtmlTable, u"Maps"_s )
10122 << new QgsStaticExpressionFunction( u"map_to_html_dl"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ),
10123 fcnMapToHtmlDefinitionList, u"Maps"_s )
10124 << new QgsStaticExpressionFunction( u"url_encode"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ),
10125 fcnToFormUrlEncode, u"Maps"_s )
10126
10127 ;
10128
10130
10131 //QgsExpression has ownership of all built-in functions
10132 for ( QgsExpressionFunction *func : std::as_const( functions ) )
10133 {
10134 *sOwnedFunctions() << func;
10135 *sBuiltinFunctions() << func->name();
10136 sBuiltinFunctions()->append( func->aliases() );
10137 }
10138 }
10139 return functions;
10140}
10141
10142bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
10143{
10144 int fnIdx = functionIndex( function->name() );
10145 if ( fnIdx != -1 )
10146 {
10147 return false;
10148 }
10149
10150 QMutexLocker locker( &sFunctionsMutex );
10151 sFunctions()->append( function );
10152 if ( transferOwnership )
10153 sOwnedFunctions()->append( function );
10154
10155 return true;
10156}
10157
10158bool QgsExpression::unregisterFunction( const QString &name )
10159{
10160 // You can never override the built in functions.
10161 if ( QgsExpression::BuiltinFunctions().contains( name ) )
10162 {
10163 return false;
10164 }
10165 int fnIdx = functionIndex( name );
10166 if ( fnIdx != -1 )
10167 {
10168 QMutexLocker locker( &sFunctionsMutex );
10169 sFunctions()->removeAt( fnIdx );
10170 sFunctionIndexMap.clear();
10171 return true;
10172 }
10173 return false;
10174}
10175
10177{
10178 const QList<QgsExpressionFunction *> &ownedFunctions = *sOwnedFunctions();
10179 for ( QgsExpressionFunction *func : std::as_const( ownedFunctions ) )
10180 {
10181 sBuiltinFunctions()->removeAll( func->name() );
10182 for ( const QString &alias : func->aliases() )
10184 sBuiltinFunctions()->removeAll( alias );
10185 }
10186
10187 sFunctions()->removeAll( func );
10188 }
10189
10190 qDeleteAll( *sOwnedFunctions() );
10191 sOwnedFunctions()->clear();
10193
10194const QStringList &QgsExpression::BuiltinFunctions()
10195{
10196 if ( sBuiltinFunctions()->isEmpty() )
10197 {
10198 Functions(); // this method builds the gmBuiltinFunctions as well
10199 }
10200 return *sBuiltinFunctions();
10202
10204 : QgsExpressionFunction( u"array_foreach"_s, QgsExpressionFunction::ParameterList() // skip-keyword-check
10205 << QgsExpressionFunction::Parameter( u"array"_s )
10206 << QgsExpressionFunction::Parameter( u"expression"_s ),
10207 u"Arrays"_s )
10208{
10209
10210}
10211
10213{
10214 bool isStatic = false;
10215
10216 QgsExpressionNode::NodeList *args = node->args();
10218 if ( args->count() < 2 )
10219 return false;
10220
10221 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10222 {
10223 isStatic = true;
10224 }
10225 return isStatic;
10226}
10227
10229{
10230 Q_UNUSED( node )
10231 QVariantList result;
10232
10233 if ( args->count() < 2 )
10234 // error
10235 return result;
10236
10237 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
10238
10239 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
10240 std::unique_ptr< QgsExpressionContext > tempContext;
10241 if ( !subContext )
10242 {
10243 tempContext = std::make_unique< QgsExpressionContext >();
10244 subContext = tempContext.get();
10245 }
10246
10247 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
10248 subContext->appendScope( subScope );
10249
10250 int i = 0;
10251 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
10252 {
10253 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, *it, true ) );
10254 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, i, true ) );
10255 result << args->at( 1 )->eval( parent, subContext );
10256 }
10257
10258 if ( context )
10259 delete subContext->popScope();
10260
10261 return result;
10262}
10263
10264QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10266 // This is a dummy function, all the real handling is in run
10267 Q_UNUSED( values )
10268 Q_UNUSED( context )
10269 Q_UNUSED( parent )
10270 Q_UNUSED( node )
10271
10272 Q_ASSERT( false );
10273 return QVariant();
10274}
10275
10277{
10278 QgsExpressionNode::NodeList *args = node->args();
10279
10280 if ( args->count() < 2 )
10281 // error
10282 return false;
10283
10284 args->at( 0 )->prepare( parent, context );
10285
10286 QgsExpressionContext subContext;
10287 if ( context )
10288 subContext = *context;
10291 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
10292 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, QVariant(), true ) );
10293 subContext.appendScope( subScope );
10294
10295 args->at( 1 )->prepare( parent, &subContext );
10296
10297 return true;
10298}
10302 << QgsExpressionFunction::Parameter( u"array"_s )
10303 << QgsExpressionFunction::Parameter( u"expression"_s )
10304 << QgsExpressionFunction::Parameter( u"limit"_s, true, 0 ),
10305 u"Arrays"_s )
10306{
10307
10308}
10309
10311{
10312 bool isStatic = false;
10313
10314 QgsExpressionNode::NodeList *args = node->args();
10316 if ( args->count() < 2 )
10317 return false;
10318
10319 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10320 {
10321 isStatic = true;
10322 }
10323 return isStatic;
10324}
10325
10327{
10328 Q_UNUSED( node )
10329 QVariantList result;
10330
10331 if ( args->count() < 2 )
10332 // error
10333 return result;
10334
10335 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
10336
10337 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
10338 std::unique_ptr< QgsExpressionContext > tempContext;
10339 if ( !subContext )
10340 {
10341 tempContext = std::make_unique< QgsExpressionContext >();
10342 subContext = tempContext.get();
10343 }
10344
10345 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
10346 subContext->appendScope( subScope );
10347
10348 int limit = 0;
10349 if ( args->count() >= 3 )
10350 {
10351 const QVariant limitVar = args->at( 2 )->eval( parent, context );
10352
10353 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
10354 {
10355 limit = limitVar.toInt();
10356 }
10357 else
10358 {
10359 return result;
10360 }
10361 }
10362
10363 for ( const QVariant &value : array )
10364 {
10365 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, value, true ) );
10366 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
10367 {
10368 result << value;
10369
10370 if ( limit > 0 && limit == result.size() )
10371 break;
10372 }
10373 }
10374
10375 if ( context )
10376 delete subContext->popScope();
10377
10378 return result;
10379}
10380
10381QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10383 // This is a dummy function, all the real handling is in run
10384 Q_UNUSED( values )
10385 Q_UNUSED( context )
10386 Q_UNUSED( parent )
10387 Q_UNUSED( node )
10388
10389 Q_ASSERT( false );
10390 return QVariant();
10391}
10392
10394{
10395 QgsExpressionNode::NodeList *args = node->args();
10396
10397 if ( args->count() < 2 )
10398 // error
10399 return false;
10400
10401 args->at( 0 )->prepare( parent, context );
10402
10403 QgsExpressionContext subContext;
10404 if ( context )
10405 subContext = *context;
10406
10408 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
10409 subContext.appendScope( subScope );
10410
10411 args->at( 1 )->prepare( parent, &subContext );
10412
10413 return true;
10416 : QgsExpressionFunction( u"with_variable"_s, QgsExpressionFunction::ParameterList() <<
10417 QgsExpressionFunction::Parameter( u"name"_s )
10418 << QgsExpressionFunction::Parameter( u"value"_s )
10419 << QgsExpressionFunction::Parameter( u"expression"_s ),
10420 u"General"_s )
10421{
10422
10423}
10424
10426{
10427 bool isStatic = false;
10428
10429 QgsExpressionNode::NodeList *args = node->args();
10430
10431 if ( args->count() < 3 )
10432 return false;
10433
10434 // We only need to check if the node evaluation is static, if both - name and value - are static.
10435 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10436 {
10437 QVariant name = args->at( 0 )->eval( parent, context );
10438 QVariant value = args->at( 1 )->eval( parent, context );
10440 // Temporarily append a new scope to provide the variable
10441 appendTemporaryVariable( context, name.toString(), value );
10442 if ( args->at( 2 )->isStatic( parent, context ) )
10443 isStatic = true;
10444 popTemporaryVariable( context );
10445 }
10446
10447 return isStatic;
10448}
10449
10451{
10452 Q_UNUSED( node )
10453 QVariant result;
10454
10455 if ( args->count() < 3 )
10456 // error
10457 return result;
10458
10459 QVariant name = args->at( 0 )->eval( parent, context );
10460 QVariant value = args->at( 1 )->eval( parent, context );
10461
10462 const QgsExpressionContext *updatedContext = context;
10463 std::unique_ptr< QgsExpressionContext > tempContext;
10464 if ( !updatedContext )
10465 {
10466 tempContext = std::make_unique< QgsExpressionContext >();
10467 updatedContext = tempContext.get();
10469
10470 appendTemporaryVariable( updatedContext, name.toString(), value );
10471 result = args->at( 2 )->eval( parent, updatedContext );
10472
10473 if ( context )
10474 popTemporaryVariable( updatedContext );
10475
10476 return result;
10477}
10478
10479QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10481 // This is a dummy function, all the real handling is in run
10482 Q_UNUSED( values )
10483 Q_UNUSED( context )
10484 Q_UNUSED( parent )
10485 Q_UNUSED( node )
10486
10487 Q_ASSERT( false );
10488 return QVariant();
10489}
10490
10492{
10493 QgsExpressionNode::NodeList *args = node->args();
10494
10495 if ( args->count() < 3 )
10496 // error
10497 return false;
10498
10499 QVariant name = args->at( 0 )->prepare( parent, context );
10500 QVariant value = args->at( 1 )->prepare( parent, context );
10501
10502 const QgsExpressionContext *updatedContext = context;
10503 std::unique_ptr< QgsExpressionContext > tempContext;
10504 if ( !updatedContext )
10505 {
10506 tempContext = std::make_unique< QgsExpressionContext >();
10507 updatedContext = tempContext.get();
10508 }
10509
10510 appendTemporaryVariable( updatedContext, name.toString(), value );
10511 args->at( 2 )->prepare( parent, updatedContext );
10512
10513 if ( context )
10514 popTemporaryVariable( updatedContext );
10515
10516 return true;
10517}
10518
10519void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
10520{
10521 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10522 delete updatedContext->popScope();
10523}
10524
10525void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
10526{
10527 QgsExpressionContextScope *scope = new QgsExpressionContextScope();
10528 scope->addVariable( QgsExpressionContextScope::StaticVariable( name, value, true ) );
10529
10530 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10531 updatedContext->appendScope( scope );
10532}
@ Left
Buffer to left of line.
Definition qgis.h:2155
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3362
@ ScaleDashOnly
Only dash lengths are adjusted.
Definition qgis.h:3364
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
Definition qgis.h:3363
@ ScaleGapOnly
Only gap lengths are adjusted.
Definition qgis.h:3365
@ Success
Operation succeeded.
Definition qgis.h:2101
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
Definition qgis.h:3074
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2254
@ Point
Points.
Definition qgis.h:366
@ Line
Lines.
Definition qgis.h:367
@ Polygon
Polygons.
Definition qgis.h:368
@ Unknown
Unknown types.
Definition qgis.h:369
@ Null
No geometry.
Definition qgis.h:370
JoinStyle
Join styles for buffers.
Definition qgis.h:2179
@ Bevel
Use beveled joins.
Definition qgis.h:2182
@ Round
Use rounded joins.
Definition qgis.h:2180
@ Miter
Use mitered joins.
Definition qgis.h:2181
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:6240
@ StdDev
Standard deviation.
Definition qgis.h:6247
@ NoStatistic
No statistic.
Definition qgis.h:6241
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:201
@ Plugin
Plugin based layer.
Definition qgis.h:196
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:202
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:199
@ Vector
Vector layer.
Definition qgis.h:194
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:198
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:197
@ Raster
Raster layer.
Definition qgis.h:195
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:200
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2166
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2168
@ Round
Round cap.
Definition qgis.h:2167
@ Square
Square cap (extends past start/end of line by buffer distance).
Definition qgis.h:2169
Aggregate
Available aggregates to calculate.
Definition qgis.h:6117
@ StringMinimumLength
Minimum length of string (string fields only).
Definition qgis.h:6134
@ FirstQuartile
First quartile (numeric fields only).
Definition qgis.h:6131
@ Mean
Mean of values (numeric fields only).
Definition qgis.h:6124
@ Median
Median of values (numeric fields only).
Definition qgis.h:6125
@ Max
Max of values.
Definition qgis.h:6122
@ Min
Min of values.
Definition qgis.h:6121
@ StringMaximumLength
Maximum length of string (string fields only).
Definition qgis.h:6135
@ Range
Range of values (max - min) (numeric and datetime fields only).
Definition qgis.h:6128
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
Definition qgis.h:6139
@ Sum
Sum of values.
Definition qgis.h:6123
@ Minority
Minority of values.
Definition qgis.h:6129
@ CountMissing
Number of missing (null) values.
Definition qgis.h:6120
@ ArrayAggregate
Create an array of values.
Definition qgis.h:6138
@ Majority
Majority of values.
Definition qgis.h:6130
@ StDevSample
Sample standard deviation of values (numeric fields only).
Definition qgis.h:6127
@ Count
Count.
Definition qgis.h:6118
@ ThirdQuartile
Third quartile (numeric fields only).
Definition qgis.h:6132
@ CountDistinct
Number of distinct values.
Definition qgis.h:6119
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
Definition qgis.h:6136
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
Definition qgis.h:6137
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only).
Definition qgis.h:6133
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3347
@ HalfDash
Start or finish the pattern with a half length dash.
Definition qgis.h:3350
@ HalfGap
Start or finish the pattern with a half length gap.
Definition qgis.h:3352
@ FullGap
Start or finish the pattern with a full gap.
Definition qgis.h:3351
@ FullDash
Start or finish the pattern with a full dash.
Definition qgis.h:3349
@ NoRule
No special rule.
Definition qgis.h:3348
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2238
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2239
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
Definition qgis.h:2240
@ Point
Point.
Definition qgis.h:282
@ PointM
PointM.
Definition qgis.h:315
@ PointZ
PointZ.
Definition qgis.h:299
@ GeometryCollection
GeometryCollection.
Definition qgis.h:289
@ PointZM
PointZM.
Definition qgis.h:331
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.
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.
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.
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...
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:46
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.
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.
Handles coordinate transforms between two 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:36
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition qgscurve.cpp:278
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:176
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:54
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:273
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.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
Ellipse geometry type.
Definition qgsellipse.h:41
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.
An abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
QgsExpressionFunction(const QString &fnname, int params, const QString &group, const QString &helpText=QString(), bool lazyEval=false, bool handlesNull=false, bool isContextual=false)
Constructor for function which uses unnamed parameters.
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.
const QString helpText() const
The help text for the function.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes its 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.
Handles 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 QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
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).
friend class QgsExpressionNodeFunction
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QgsExpression(const QString &expr)
Creates a new expression based on the provided string.
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.
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:60
QgsFields fields
Definition qgsfeature.h:70
QgsFeatureId id
Definition qgsfeature.h:68
QgsGeometry geometry
Definition qgsfeature.h:71
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:65
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:751
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.
QgsGeometry mergeLines(const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
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.
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.
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:141
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2807
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
static QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
static QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
A representation of the interval between two datetime values.
Definition qgsinterval.h:50
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.
Represents a model of the Earth's magnetic field.
static bool fieldComponentsWithTimeDerivatives(double Bx, double By, double Bz, double Bxt, double Byt, double Bzt, double &H, double &F, double &D, double &I, double &Ht, double &Ft, double &Dt, double &It)
Compute various quantities dependent on a magnetic field and their rates of change.
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:83
QString name
Definition qgsmaplayer.h:87
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:90
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:86
QgsLayerMetadata metadata
Definition qgsmaplayer.h:89
Qgis::LayerType type
Definition qgsmaplayer.h:93
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:96
Implementation of a geometry simplifier 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.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:244
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:716
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:582
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:450
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:129
double z
Definition qgspoint.h:58
double x
Definition qgspoint.h:56
double m
Definition qgspoint.h:59
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:728
double y
Definition qgspoint.h:57
QgsRelationManager * relationManager
Definition qgsproject.h:124
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.
bool isRamp() const
Returns true if the field carries a color ramp component information (RedMin/RedMax,...
bool isColor() const
Returns true if the field carries a color component (Red, Green, Blue and optionally Alpha) informati...
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:42
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:50
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:47
Q_INVOKABLE 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.
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.
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 unaccent(const QString &input)
Removes accents and other diacritical marks from a string, replacing accented characters with their u...
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:505
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:150
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.
Allows creation of a multi-layer database-side transaction.
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 dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
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
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
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.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
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 Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
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).
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:596
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7486
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7485
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:6976
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6935
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
const QString cacheKey(const QString &pathIn)
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
double qDateTimeToDecimalYear(const QDateTime &dateTime)
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.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:98
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
QLineF segment(int index, QRectF rect, double radius)
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:64
const QgsMapLayer * layer
Definition qgsogcutils.h:74
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:75
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:34